package com.authine.cloudpivot.web.api.controller.runtime;

import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import javax.validation.constraints.NotBlank;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.curator.shaded.com.google.common.collect.Sets;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.util.Assert;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.authine.cloudpivot.engine.api.model.application.AppFunctionModel;
import com.authine.cloudpivot.engine.api.model.application.AppPackageModel;
import com.authine.cloudpivot.engine.api.model.bizform.BizFormModel;
import com.authine.cloudpivot.engine.api.model.bizmodel.BizSchemaModel;
import com.authine.cloudpivot.engine.api.model.im.MessageHistoryModel;
import com.authine.cloudpivot.engine.api.model.organization.DepartmentModel;
import com.authine.cloudpivot.engine.api.model.organization.UserModel;
import com.authine.cloudpivot.engine.api.model.permission.AdminModel;
import com.authine.cloudpivot.engine.api.model.runtime.BizCommentModel;
import com.authine.cloudpivot.engine.api.model.runtime.BizObjectCreatedModel;
import com.authine.cloudpivot.engine.api.model.runtime.BizObjectDataItemModel;
import com.authine.cloudpivot.engine.api.model.runtime.BizObjectModel;
import com.authine.cloudpivot.engine.api.model.runtime.CirculateItemModel;
import com.authine.cloudpivot.engine.api.model.runtime.ParticipantModel;
import com.authine.cloudpivot.engine.api.model.runtime.SelectionValue;
import com.authine.cloudpivot.engine.api.model.runtime.WorkItemModel;
import com.authine.cloudpivot.engine.api.model.runtime.WorkflowExceptionLogDetailModel;
import com.authine.cloudpivot.engine.api.model.runtime.WorkflowInstanceModel;
import com.authine.cloudpivot.engine.api.model.system.BizObjectLogModel;
import com.authine.cloudpivot.engine.api.model.workflow.SingleActivityWorkflowTemplateModel;
import com.authine.cloudpivot.engine.api.model.workflow.WorkflowTemplateHeaderModel;
import com.authine.cloudpivot.engine.api.model.workflow.WorkflowTemplateModel;
import com.authine.cloudpivot.engine.api.model.workflow.WorkflowTokenModel;
import com.authine.cloudpivot.engine.api.model.workflow.WorkflowTreeModel;
import com.authine.cloudpivot.engine.api.model.workflow.WorkflowTrustModel;
import com.authine.cloudpivot.engine.api.model.workflow.WorkflowTrustRuleModel;
import com.authine.cloudpivot.engine.enums.ErrCode;
import com.authine.cloudpivot.engine.enums.status.ActivityStatus;
import com.authine.cloudpivot.engine.enums.status.WorkItemStatus;
import com.authine.cloudpivot.engine.enums.status.WorkflowExceptionLogStatus;
import com.authine.cloudpivot.engine.enums.status.WorkflowInstanceStatus;
import com.authine.cloudpivot.engine.enums.status.WorkflowTokenStatus;
import com.authine.cloudpivot.engine.enums.status.WorkflowTrustRuleState;
import com.authine.cloudpivot.engine.enums.type.AdminType;
import com.authine.cloudpivot.engine.enums.type.CirculateResource;
import com.authine.cloudpivot.engine.enums.type.DefaultPropertyType;
import com.authine.cloudpivot.engine.enums.type.IMMessageChannelType;
import com.authine.cloudpivot.engine.enums.type.IMMessageType;
import com.authine.cloudpivot.engine.enums.type.OperationType;
import com.authine.cloudpivot.engine.enums.type.RangeType;
import com.authine.cloudpivot.engine.enums.type.TrustType;
import com.authine.cloudpivot.engine.enums.type.UnitType;
import com.authine.cloudpivot.engine.enums.type.WorkflowTrustType;
import com.authine.cloudpivot.engine.workflow.enums.ActivityType;
import com.authine.cloudpivot.engine.workflow.model.activity.Activity;
import com.authine.cloudpivot.engine.workflow.model.activity.CirculateActivity;
import com.authine.cloudpivot.engine.workflow.model.activity.ParticipantActivity;
import com.authine.cloudpivot.engine.workflow.model.activity.SubInstanceActivity;
import com.authine.cloudpivot.engine.workflow.model.activity.SystemActivity;
import com.authine.cloudpivot.web.api.cache.RedisCacheService;
import com.authine.cloudpivot.web.api.dubbo.DubboConfigService;
import com.authine.cloudpivot.web.api.exception.PortalException;
import com.authine.cloudpivot.web.api.exception.ResultEnum;
import com.authine.cloudpivot.web.api.handler.CustomizedOrigin;
import com.authine.cloudpivot.web.api.helper.WorkflowInstanceHelper;
import com.authine.cloudpivot.web.api.helper.WorkflowInstanceOptions;
import com.authine.cloudpivot.web.api.helper.WorkflowInstanceStatusHelper;
import com.authine.cloudpivot.web.api.util.AuthUtils;
import com.authine.cloudpivot.web.api.view.PageVO;
import com.authine.cloudpivot.web.api.view.ResponseResult;
import com.authine.cloudpivot.web.api.view.app.AppFunctionVO;
import com.authine.cloudpivot.web.api.view.app.AppPackageVO;
import com.authine.cloudpivot.web.api.view.runtime.ActivityPreprocessorVO;
import com.authine.cloudpivot.web.api.view.runtime.ActivityStatusVO;
import com.authine.cloudpivot.web.api.view.runtime.CirculateSendToVO;
import com.authine.cloudpivot.web.api.view.runtime.CirculateitemVO;
import com.authine.cloudpivot.web.api.view.runtime.CompareOperationLogVO;
import com.authine.cloudpivot.web.api.view.runtime.FormDataVO;
import com.authine.cloudpivot.web.api.view.runtime.FormPermissionVO;
import com.authine.cloudpivot.web.api.view.runtime.TransferResultVO;
import com.authine.cloudpivot.web.api.view.runtime.TransferVO;
import com.authine.cloudpivot.web.api.view.runtime.TrustOriginatorVO;
import com.authine.cloudpivot.web.api.view.runtime.WorkItemAllVO;
import com.authine.cloudpivot.web.api.view.runtime.WorkItemApprovalVO;
import com.authine.cloudpivot.web.api.view.runtime.WorkItemRejectVO;
import com.authine.cloudpivot.web.api.view.runtime.WorkItemSendToVO;
import com.authine.cloudpivot.web.api.view.runtime.WorkItemVO;
import com.authine.cloudpivot.web.api.view.runtime.WorkflowInstanceVO;
import com.authine.cloudpivot.web.api.view.runtime.WorkflowIntanceBaseInfoVO;
import com.authine.cloudpivot.web.api.view.runtime.WorkflowIntanceLogInfoVO;
import com.authine.cloudpivot.web.api.view.runtime.WorkflowTemplateChartVO;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import io.netty.util.concurrent.DefaultThreadFactory;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;

@Api(description = "流程操作类", tags = "运行时::流程实例接口")
@RestController
@RequestMapping("/api/runtime/workflow")
@Slf4j
@Validated
@CustomizedOrigin(level = 1)
@SuppressWarnings("unused")
public class WorkflowInstanceController extends WorkflowInstenceRuntimeController {

    @Autowired
    private RedisCacheService redisCacheService;

    @Autowired
    private DubboConfigService dubboConfigService;

    private static final ThreadPoolExecutor transferPoolExecutor = new ThreadPoolExecutor(5, 20, 60L, TimeUnit.SECONDS,
        new LinkedBlockingQueue<>(), new DefaultThreadFactory("TRANSFER"));

    private volatile boolean isProc = false;

    @ApiOperation(value = "根据流程实例id查询表单数据")
    @ApiImplicitParams({@ApiImplicitParam(name = "workflowInstanceId", value = "流程实例ID", dataType = "String",
        paramType = "query", required = true)})
    @GetMapping("/getBizObjectByWfInstanceId")
    public ResponseResult<BizObjectModel> getBizObjectByWfInstanceId(
        @RequestParam(value = "workflowInstanceId", required = true) String workflowInstanceId) {
        WorkflowInstanceModel workflowInstance = getWorkflowInstanceFacade().getWorkflowInstance(workflowInstanceId);
        if (workflowInstance == null) {
            throw new PortalException(ErrCode.WORKFLOW_INS_EMPTY);
        }
        WorkflowTemplateHeaderModel workflowTemplateHeaderModel =
            getAppManagementFacade().getWorkflowTemplateHeader(workflowInstance.getWorkflowCode());
        if (workflowTemplateHeaderModel == null) {
            throw new PortalException(ErrCode.WORKFLOW_INS_EMPTY);
        }
        String userId = getUserId();
        String objectId = workflowInstance.getBizObjectId();
        String schemaCode = workflowTemplateHeaderModel.getSchemaCode();
        BizObjectCreatedModel bizObjectCreatedModel = getBizObjectFacade().getBizObject(userId, schemaCode, objectId);
        return getOkResponseResult(bizObjectCreatedModel, "根据流程实例id查询表单数据成功");
    }

    @ApiOperation(value = "驳回待办任务接口")
    @PostMapping("/reject_workItem")
    public ResponseResult<Boolean> reject(@RequestBody WorkItemRejectVO workItemReject) {
        return this.handleReject(workItemReject);
    }

    @ApiOperation(value = "转办待办任务")
    @PostMapping("/forward_workItem")
    public ResponseResult<Boolean> forwardWorkItem(@RequestBody WorkItemSendToVO workItemSendTo) {
        return this.handleForwardWorkItem(workItemSendTo);
    }

    @ApiOperation(value = "协办待办任务")
    @PostMapping("/assist_workItem")
    public ResponseResult<Boolean> assistWorkItem(@RequestBody WorkItemSendToVO workItemSendTo) {
        validateParticipantors(workItemSendTo.getParticipantors());
        validateWorkItem(workItemSendTo.getWorkItemId(), "当前任务已被取消或者已被处理");
        validateAssistParticipant(workItemSendTo.getWorkItemId(), workItemSendTo.getParticipantors(), "不能协办给当前活动处理人");

        Boolean assistResult = assistWorkItem(getUserId(), workItemSendTo.getWorkItemId(),
            workItemSendTo.getParticipantors(), workItemSendTo.getComment());
        writeOperationLog(workItemSendTo.getWorkflowInstanceId(), workItemSendTo.getWorkItemId(),
            workItemSendTo.getActivityCode(), OperationType.ASSIST, null);
        if (assistResult) {
            return getOkResponseResult(assistResult, "成功调用协办待办任务接口");
        }
        log.debug("协办待办任务失败, workItemId = {}, participantors = {}", workItemSendTo.getWorkItemId(),
            JSON.toJSONString(workItemSendTo.getParticipantors()));
        return getOkResponseResult(assistResult, "协办待办任务失败");
    }

    @ApiOperation(value = "传阅待办任务")
    @PostMapping("/circulate_workItem")
    public ResponseResult<Boolean> circulateWorkItem(@RequestBody WorkItemSendToVO workItemSendTo) {
        validateParticipantors(workItemSendTo.getParticipantors());
        validateNotEmpty(workItemSendTo.getWorkItemId(), "任务id不能为空");

        if (workItemSendTo.getParticipantors().size() > 50) {
            return getErrResponseResult(null, 402519L, "传阅人数不能超过50人");
        }

        Boolean circulateResult = circulateWorkItem(getUserId(), workItemSendTo.getWorkItemId(),
            workItemSendTo.getParticipantors(), workItemSendTo.getComment());
        writeOperationLog(workItemSendTo.getWorkflowInstanceId(), workItemSendTo.getWorkItemId(),
            workItemSendTo.getActivityCode(), OperationType.CIRCULATE, null);
        if (circulateResult) {
            return getOkResponseResult(circulateResult, "成功调用传阅待办任务接口");
        }
        log.debug("传阅待办任务失败, workItemId = {}, participantors = {}", workItemSendTo.getWorkItemId(),
            JSON.toJSONString(workItemSendTo.getParticipantors()));
        return getOkResponseResult(circulateResult, "传阅待办任务失败");
    }

    @ApiOperation(value = "加签待办任务")
    @PostMapping("/adjust_workItem_participators")
    public ResponseResult<Boolean> adjustWorkItemParticipators(@RequestBody WorkItemSendToVO workItemSendTo) {
        validateParticipantors(workItemSendTo.getParticipantors());
        validateWorkItem(workItemSendTo.getWorkItemId(), "当前任务已被取消或者已被处理");

        Boolean countersignResult = adjustWorkItemParticipants(getUserId(), workItemSendTo.getWorkItemId(),
            workItemSendTo.getParticipantors(), workItemSendTo.getComment());
        writeOperationLog(workItemSendTo.getWorkflowInstanceId(), workItemSendTo.getWorkItemId(),
            workItemSendTo.getActivityCode(), OperationType.ADD_WORKITEM, null);
        if (countersignResult) {
            return getOkResponseResult(countersignResult, "成功调用加签待办任务");
        }
        log.debug("加签待办任务失败, workItemId = {}, participantors = {}", workItemSendTo.getWorkItemId(),
            JSON.toJSONString(workItemSendTo.getParticipantors()));
        return getOkResponseResult(countersignResult, "加签待办任务失败");
    }

    @ApiOperation(value = "撤回任务")
    @ApiImplicitParams({
        @ApiImplicitParam(name = "workflowInstanceId", value = "流程实例id", required = true, dataType = "String",
            paramType = "query"),
        @ApiImplicitParam(name = "activityCode", value = "节点编码", required = true, dataType = "String",
            paramType = "query"),
        @ApiImplicitParam(name = "workItemId", value = "流程任务id", required = true, dataType = "String",
            paramType = "query")})
    @GetMapping("/revoke_workitem")
    public ResponseResult<Boolean> revokeWorkItem(String workflowInstanceId, String activityCode, String workItemId) {
        return this.handleRevokeWorkItem(workflowInstanceId, activityCode, workItemId);
    }

    @ApiOperation(value = "激活指定活动")
    @ApiImplicitParams({
        @ApiImplicitParam(name = "workflowInstanceId", value = "流程实例id", required = true, dataType = "String",
            paramType = "query"),
        @ApiImplicitParam(name = "activityCode", value = "节点编码", required = true, dataType = "String",
            paramType = "query")})
    @GetMapping("/activate_activity")
    public ResponseResult<Boolean> activateActivity(String workflowInstanceId, String activityCode) {
        validateNotEmpty(workflowInstanceId, "流程实例id为空");
        validateNotEmpty(activityCode, "节点编码为空");

        log.debug("激活指定活动：workflowInstanceId = {}, activityCode = {}", workflowInstanceId, activityCode);
        Boolean activateResult = activateActivity(getUserId(), workflowInstanceId, activityCode);
        writeOperationLog(workflowInstanceId, null, activityCode, OperationType.ADMIN_ACTIVITION_NODE, null);
        if (activateResult) {
            return getOkResponseResult(activateResult, "活动激活成功");
        }
        return getOkResponseResult(activateResult, "活动激活失败");
    }

    @ApiOperation(value = "调整当前活动处理人")
    @PostMapping("/adjust_participantors")
    public ResponseResult<Boolean> adjustParticipants(@RequestBody WorkItemSendToVO workItemSendTo) {
        validateNotEmpty(workItemSendTo.getWorkflowInstanceId(), "流程实例id为空");
        validateNotEmpty(workItemSendTo.getActivityCode(), "节点编码为空");
        validateParticipantors(workItemSendTo.getParticipantors());
        // 已办
        List<WorkItemModel> workItemModels =
            getWorkflowInstanceFacade().getWorkItems(workItemSendTo.getWorkflowInstanceId(), false);
        List<Map<String, String>> beforeAdjustParticipantors = Lists.newArrayList();// 调整前参与者
        if (CollectionUtils.isNotEmpty(workItemModels)) {
            workItemModels.stream().forEach(workItemModel -> {
                if (workItemSendTo.getActivityCode().equals(workItemModel.getActivityCode())) {
                    Map<String, String> participantMap = Maps.newHashMap();
                    participantMap.put("participant", workItemModel.getParticipant());
                    participantMap.put("participantName", workItemModel.getParticipantName());
                    beforeAdjustParticipantors.add(participantMap);
                }
            });
        }
        log.debug("调整当前活动处理人：{}", JSON.toJSONString(workItemSendTo));
        Boolean adjustResult = adjustActivityParticipants(getUserId(), workItemSendTo.getWorkflowInstanceId(),
            workItemSendTo.getActivityCode(), workItemSendTo.getParticipantors());
        writeOperationLogForAdjust(workItemSendTo.getWorkflowInstanceId(), workItemSendTo.getWorkItemId(),
            workItemSendTo.getActivityCode(), OperationType.ADMIN_ADJUST_PROCESSING_PERSON, beforeAdjustParticipantors,
            workItemSendTo.getParticipantors());
        if (adjustResult) {
            return getOkResponseResult(adjustResult, "活动处理人调整成功");
        }
        return getOkResponseResult(adjustResult, "活动处理人调整失败");
    }

    @ApiOperation(value = "根据流程实例id查询对应流程的节点")
    @ApiImplicitParam(name = "workflowInstanceId", value = "流程实例id", required = true, dataType = "String",
        paramType = "query")
    @GetMapping("/list_workflow_instance_activity")
    public ResponseResult<List<Map<String, Object>>> listWorkflowInstanceActivity(String workflowInstanceId) {
        List<Map<String, Object>> result = Lists.newArrayList();
        WorkflowInstanceModel instanceModel = getWorkflowInstanceFacade().getWorkflowInstance(workflowInstanceId);
        if (instanceModel == null) {
            return getOkResponseResult(Lists.newArrayList(), "流程实例不存在");
        }

        List<WorkItemModel> workItems = getWorkflowInstanceFacade().getWorkItems(workflowInstanceId, false);
        List<WorkItemModel> finishedItems = getWorkflowInstanceFacade().getWorkItems(workflowInstanceId, true);
        Map<String, List<WorkItemModel>> runtimeItemMap =
            workItems.stream().collect(Collectors.groupingBy(WorkItemModel::getActivityCode));
        Map<String, List<WorkItemModel>> finishedItemMap =
            finishedItems.stream().collect(Collectors.groupingBy(WorkItemModel::getActivityCode));
        WorkflowTemplateModel templateModel = getAppManagementFacade()
            .getPublishedWorkflowTemplate(instanceModel.getWorkflowCode(), instanceModel.getWorkflowVersion());
        Map<String,
            String> activityCodeMap = templateModel.getActivities().stream()
                .filter(model -> model.getActivityType() == ActivityType.PARTICIPANT)
                .collect(Collectors.toMap(Activity::getActivityCode, Activity::getActivityName));

        ParticipantActivity originateActivity = templateModel.getOriginateActivity();
        String originateActivityCode = originateActivity == null ? null : originateActivity.getActivityCode();
        boolean isExternalUser = isExternalUser(instanceModel.getOriginator());// 是否是外部用户

        activityCodeMap.forEach((activityCode, activityName) -> {
            Map<String, Object> dataMap = Maps.newHashMap();
            dataMap.put("activityCode", activityCode);
            dataMap.put("activityName", activityName);

            if (runtimeItemMap.containsKey(activityCode)) {
                dataMap.put("participantors", getActivityParticipant(runtimeItemMap, activityCode));
                dataMap.put("status", 1);
            } else if (finishedItemMap.containsKey(activityCode)) {
                dataMap.put("participantors", getActivityParticipant(finishedItemMap, activityCode));
                dataMap.put("status", 0);
            } else {
                dataMap.put("participantors", null);
                dataMap.put("status", 0);
            }
            if (isExternalUser && activityCode.equals(originateActivityCode)) {// 外部用户发起的流程的发起节点不能被激活
                dataMap.put("status", 0);// 不能选择
            }
            result.add(dataMap);
        });

        return getOkResponseResult(result, "数据加载成功");
    }

    /**
     * 获取节点的参与者（待办已办）
     *
     * @param dataMap
     *            待办已办 数据集合
     * @param activityCode
     *            节点编码
     * @return 节点参与者
     */
    private List<Map<String, String>> getActivityParticipant(Map<String, List<WorkItemModel>> dataMap,
        String activityCode) {
        List<Map<String, String>> partList = Lists.newArrayList();
        dataMap.get(activityCode).stream().collect(Collectors.groupingBy(WorkItemModel::getParticipant))
            .forEach((id, items) -> {
                Map<String, String> partMap = Maps.newHashMap();
                partMap.put("id", id);
                for (WorkItemModel model : items) {
                    if (id.equals(model.getParticipant())) {
                        partMap.put("name", model.getParticipantName());
                        break;
                    }
                }
                partList.add(partMap);
            });
        return partList;
    }

    @ApiOperation(value = "取消当前活动所有代办任务")
    @ApiImplicitParams({
        @ApiImplicitParam(name = "workflowInstanceId", value = "流程实例id", required = true, dataType = "String",
            paramType = "query"),
        @ApiImplicitParam(name = "activityCode", value = "节点编码", required = true, dataType = "String",
            paramType = "query")})
    @GetMapping("/cancel_activity")
    public ResponseResult<Boolean> cancelActivity(@RequestParam String workflowInstanceId,
        @RequestParam String activityCode) {
        validateNotEmpty(workflowInstanceId, "流程实例id为空");
        validateNotEmpty(activityCode, "节点编码为空");

        log.debug("取消当前活动所有代办任务：workflowInstanceId = {}, activityCode = {}", workflowInstanceId, activityCode);
        Boolean cancelResult = cancelActivity(getUserId(), workflowInstanceId, activityCode);
        writeOperationLog(workflowInstanceId, null, activityCode, OperationType.ADMIN_CANCEL_ALL_TASK, null);
        if (cancelResult) {
            return getOkResponseResult(cancelResult, "节点任务取消成功");
        }
        return getOkResponseResult(cancelResult, "节点任务取消失败");
    }

    @ApiOperation(value = "结束流程实例")
    @ApiImplicitParam(name = "workflowInstanceId", value = "流程实例id", required = true, dataType = "String",
        paramType = "query")
    @GetMapping("/finish_instance")
    public ResponseResult<Boolean> finishWorkflowInstance(@RequestParam String workflowInstanceId) {
        validateNotEmpty(workflowInstanceId, "流程实例id为空");
        log.debug("结束流程实例：workflowInstanceId = {}", workflowInstanceId);

        String userId = getUserId();
        Boolean finishedResult = finishWorkflowInstance(userId, workflowInstanceId);
        writeOperationLog(workflowInstanceId, null, null, OperationType.FINISH_WORKFLOW_INSTANCE, null);
        if (finishedResult) {
            return getOkResponseResult(finishedResult, "流程实例结束成功");
        }
        return getOkResponseResult(finishedResult, "流程实例结束失败");
    }

    @ApiOperation(value = "删除流程实例")
    @ApiImplicitParam(name = "workflowInstanceId", value = "流程实例id", required = true, dataType = "String",
        paramType = "query")
    @DeleteMapping("/delete_instance")
    public ResponseResult<Boolean> deleteWorkflowInstance(@RequestParam String workflowInstanceId) {
        return this.handleDeleteWorkflowInstance(workflowInstanceId);
    }

    @ApiOperation(value = "作废流程实例")
    @ApiImplicitParam(name = "workflowInstanceId", value = "流程实例id", required = true, dataType = "String",
        paramType = "query")
    @GetMapping("/abort_instance")
    public ResponseResult<Boolean> cancelWorkflowInstance(@RequestParam String workflowInstanceId) {
        return this.handlerCancelWorkflowInstance(workflowInstanceId);
    }

    @ApiOperation(value = "根据传入参数参数区分是批量已阅还是全部已阅，如果接口传入待阅任务id，则根据id更新待阅状态；否则将当前用户下所有待阅任务更新为已阅")
    @PostMapping("/update_circulate_readed")
    public ResponseResult<Boolean> updateCirculateReaded(@RequestBody CirculateSendToVO circulateSendTo) {
        log.debug("阅读任务：{}", JSON.toJSONString(circulateSendTo));
        Boolean circulateResult = updateCirculateReaded(getUserId(), circulateSendTo.getCirculateItemIds());
        if (circulateResult) {
            return getOkResponseResult(circulateResult, "待阅任务已阅成功");
        }
        return getOkResponseResult(circulateResult, "待阅任务已阅失败");
    }

    @GetMapping("/search_workitems")
    @ApiOperation(value = "查询待办列表", notes = "根据流程名称或发起人查询代办列表数据")
    @ApiImplicitParams({
        @ApiImplicitParam(name = "wd", value = "关键字。流程名称或发起人名称", required = false, dataType = "String",
            paramType = "query"),
        @ApiImplicitParam(name = "appCode", value = "模型编码", required = false, dataType = "String", paramType = "query"),
        @ApiImplicitParam(name = "workflowName", value = "流程名称", required = false, dataType = "String",
            paramType = "query"),
        @ApiImplicitParam(name = "workflowCode", value = "流程编码", required = false, dataType = "String",
            paramType = "query"),
        @ApiImplicitParam(name = "originator", value = "发起人名称", required = false, dataType = "String",
            paramType = "query"),
        @ApiImplicitParam(name = "unitType", value = "组织用户类型,1: 部门， 2：角色，3：用户", required = false, dataType = "Integer",
            paramType = "query"),
        @ApiImplicitParam(name = "batchOperate", value = "有批量处理权限的任务", required = false, dataType = "Boolean",
            paramType = "query"),
        @ApiImplicitParam(name = "page", value = "当前页码", required = false, dataType = "Integer", paramType = "query"),
        @ApiImplicitParam(name = "size", value = "每页显示数量", required = false, dataType = "Integer", paramType = "query"),
        @ApiImplicitParam(name = "activityName", value = "节点名称", required = false, dataType = "String",
            paramType = "query"),
        @ApiImplicitParam(name = "participantName", value = "处理人", required = false, dataType = "String",
            paramType = "query"),
        @ApiImplicitParam(name = "participant", value = "处理人id", required = false, dataType = "String",
            paramType = "query"),
        @ApiImplicitParam(name = "sequenceNo", value = "单据号", required = false, dataType = "String",
            paramType = "query")})
    public ResponseResult<PageVO<WorkItemVO>> searchWorkItems(String wd, String appCode, String workflowName,
        String workflowCode, String originator, Integer unitType, Boolean batchOperate, Integer page, Integer size,
        String activityName, String participantName, String participant, String sequenceNo) {
        String userId = getUserId();
        return handleSearchWorkItems(userId, wd, page, size, appCode, null, null, workflowName, workflowCode,
            originator, unitType, batchOperate, activityName, participantName, participant, sequenceNo);
    }

    // /**
    // * 流程名称
    // *
    // * @param workflowCode 流程编码
    // * @return 流程国际化名称
    // */
    // private Map<String, String> getWorkflowNames(String workflowCode) {
    // WorkflowTemplateHeaderModel headerModel = getAppManagementFacade().getWorkflowTemplateHeader(workflowCode);
    // Map<String, String> map = new HashMap<>();
    //
    // if (headerModel != null) {
    // map.put("workflowName", headerModel.getWorkflowName());
    // map.put("name_i18n", headerModel.getName_i18n());
    // }
    // return map;
    // }

    @GetMapping("/search_circulates")
    @ApiOperation(value = "查询待阅列表", notes = "根据流程名称或发起人查询待阅列表数据")
    @ApiImplicitParams({
        @ApiImplicitParam(name = "appCode", value = "模型编码", required = false, dataType = "String", paramType = "query"),
        @ApiImplicitParam(name = "wd", value = "关键字。流程名称或发起人名称", required = false, dataType = "String",
            paramType = "query"),
        @ApiImplicitParam(name = "workflowName", value = "流程名称", required = false, dataType = "String",
            paramType = "query"),
        @ApiImplicitParam(name = "workflowCode", value = "流程编码", required = false, dataType = "String",
            paramType = "query"),
        @ApiImplicitParam(name = "originator", value = "发起人名称", required = false, dataType = "String",
            paramType = "query"),
        @ApiImplicitParam(name = "unitType", value = "组织用户类型,1: 部门， 2：角色，3：用户", required = false, dataType = "Integer",
            paramType = "query"),
        @ApiImplicitParam(name = "sequenceNo", value = "单据号", required = false, dataType = "String",
            paramType = "query"),
        @ApiImplicitParam(name = "page", value = "当前页码", required = false, dataType = "Integer", paramType = "query"),
        @ApiImplicitParam(name = "size", value = "每页显示数量", required = false, dataType = "Integer",
            paramType = "query")})
    public ResponseResult<PageVO<CirculateitemVO>> searchCirculateItems(String wd, String appCode, String workflowName,
        String workflowCode, String originator, Integer unitType, String sequenceNo, Integer page, Integer size) {
        log.debug("查询待阅列表：wd = {}", wd);

        // 根据创建时间降序
        Pageable pageable = getPageable(page, size, Sort.Direction.DESC, "startTime");
        Page<CirculateItemModel> circulateItemPage = searchCirculateItems(getUserId(), appCode, wd, workflowName,
            workflowCode, originator, unitType, sequenceNo, pageable);
        if (circulateItemPage == null) {
            return getOkResponseResult(null, "查询待阅数据为空");
        }

        List<CirculateItemModel> circulateItemModels = circulateItemPage.getContent();

        List<CirculateitemVO> circulateItemVos = model2voWithCirculate(circulateItemModels);

        Page<CirculateitemVO> circulateItemVoPage =
            new PageImpl<>(circulateItemVos, pageable, circulateItemPage.getTotalElements());
        return getOkResponseResult(new PageVO<>(circulateItemVoPage), "查询待阅数据成功");
    }

    private void setCirculateitemVo(Map<String, WorkflowTemplateModel> workflowTemplateModelMap,
        CirculateItemModel circulateItemModel, CirculateitemVO circulateItemVo) {
        String key = circulateItemModel.getWorkflowCode() + "_" + circulateItemModel.getWorkflowVersion();
        WorkflowTemplateModel template;
        if (workflowTemplateModelMap.containsKey(key)) {
            template = workflowTemplateModelMap.get(key);
        } else {
            template = getAppManagementFacade().getSimplePublishedWorkflowTemplate(circulateItemModel.getWorkflowCode(),
                circulateItemModel.getWorkflowVersion());
            workflowTemplateModelMap.put(key, template);
        }
        circulateItemVo.setWorkflowName(template == null ? StringUtils.EMPTY : template.getWorkflowName());
        circulateItemVo.setName_i18n(template == null ? StringUtils.EMPTY : template.getName_i18n());

        Activity activity = template.getActivities().stream()
            .filter(t -> t.getActivityCode().equals(circulateItemModel.getActivityCode())).collect(Collectors.toList())
            .get(0);
        if (activity instanceof CirculateActivity) {
            circulateItemVo.setActivitySource(true);
        } else {
            circulateItemVo.setActivitySource(false);
        }
        circulateItemVo.setSourceName_i18n(activity == null ? null : activity.getName_i18n());
    }

    private List<CirculateitemVO> model2voWithCirculate(List<CirculateItemModel> circulateItemModels) {
        Set<String> originatorDeptIds =
            circulateItemModels.stream().map(WorkItemModel::getDepartmentId).collect(Collectors.toSet());
        Map<String, List<DepartmentModel>> parentDepts =
            getOrganizationFacade().getDepartmentsByParentIds(Lists.newArrayList(originatorDeptIds));
        // boolean isMobile = isMobile();
        final boolean isMobile = true;
        Map<String, UserModel> userModelMap = null;
        if (isMobile) {
            List<String> originatorIds =
                circulateItemModels.stream().map(WorkItemModel::getOriginator).collect(Collectors.toList());
            userModelMap = getOrganizationFacade().getUsers(originatorIds);
        }
        final Map<String, UserModel> originatorMap = userModelMap;

        Map<String, WorkflowTemplateModel> workflowTemplateModelMap = Maps.newHashMap();
        List<CirculateitemVO> circulateItemVos =
            circulateItemModels.stream().filter(Objects::nonNull).map(circulateItemModel -> {
                if (!org.springframework.util.CollectionUtils.isEmpty(parentDepts)) {
                    circulateItemModel.setDepartmentName(getFullDepartmentName(parentDepts,
                        circulateItemModel.getDepartmentId(), circulateItemModel.getDepartmentName()));
                }
                CirculateitemVO circulateItemVo = new CirculateitemVO(circulateItemModel);
                setCirculateitemVo(workflowTemplateModelMap, circulateItemModel, circulateItemVo);
                if (isMobile) {
                    circulateItemVo.setImgUrl(getUserImg(originatorMap, circulateItemModel.getOriginator()));
                }
                circulateItemVo.setSequenceNo(circulateItemModel.getSequenceNo());
                return circulateItemVo;
            }).collect(Collectors.toList());
        workflowTemplateModelMap.clear();
        return circulateItemVos;
    }

    // private Map<String, Object> getSourceName_i18n(CirculateItemModel circulateItemModel) {
    // Map<String, Object> map = new HashMap<>();
    // WorkflowTemplateModel template =
    // getAppManagementFacade().getPublishedWorkflowTemplate(circulateItemModel.getWorkflowCode(),
    // circulateItemModel.getWorkflowVersion());
    // Activity activity = template.getActivities().stream().filter(t ->
    // t.getActivityCode().equals(circulateItemModel.getActivityCode())).collect(Collectors.toList()).get(0);
    //
    // if (activity instanceof CirculateActivity) {
    // map.put("SourceName_i18ns", activity.getName_i18n());
    // map.put("isActivitySource", true);
    // return map;
    // }
    // map.put("isActivitySource", false);
    // map.put("SourceName_i18ns", activity.getName_i18n());
    // return map;
    // }

    @GetMapping("/list_finished_workitems")
    @ApiOperation(value = "查询已办列表", notes = "查询已办列表数据")
    @ApiImplicitParams({
        @ApiImplicitParam(name = "workflowName", value = "流程名称", required = false, dataType = "String",
            paramType = "query"),
        @ApiImplicitParam(name = "workflowCode", value = "流程编码", required = false, dataType = "String",
            paramType = "query"),
        @ApiImplicitParam(name = "originator", value = "发起人名称", required = false, dataType = "String",
            paramType = "query"),
        @ApiImplicitParam(name = "startTime", value = "开始-任务处理时间", required = false, dataType = "String",
            paramType = "query"),
        @ApiImplicitParam(name = "endTime", value = "结束-任务处理时间", required = false, dataType = "String",
            paramType = "query"),
        @ApiImplicitParam(name = "instanceState", value = "流程实例状态", required = false, dataType = "String",
            paramType = "query"),
        @ApiImplicitParam(name = "appCode", value = "应用编码", required = false, dataType = "String", paramType = "query"),
        @ApiImplicitParam(name = "unitType", value = "组织用户类型", required = false, dataType = "Integer",
            paramType = "query"),
        @ApiImplicitParam(name = "page", value = "当前页码", required = false, dataType = "Integer", paramType = "query"),
        @ApiImplicitParam(name = "size", value = "每页显示数量", required = false, dataType = "Integer", paramType = "query"),
        @ApiImplicitParam(name = "activityName", value = "节点名称", required = false, dataType = "String",
            paramType = "query"),
        @ApiImplicitParam(name = "participantName", value = "处理人", required = false, dataType = "String",
            paramType = "query"),
        @ApiImplicitParam(name = "participant", value = "处理人id", required = false, dataType = "String",
            paramType = "query"),
        @ApiImplicitParam(name = "sequenceNo", value = "单据号", required = false, dataType = "String",
            paramType = "query")})
    public ResponseResult<PageVO<WorkItemVO>> listFinishedWorkItems(String workflowName, String workflowCode,
        String originator, String startTime, String endTime, WorkflowInstanceStatus instanceState, Integer page,
        Integer size, String appCode, Integer unitType, String activityName, String participantName, String participant,
        String sequenceNo) {
        UnitType type = unitType == null ? UnitType.USER : UnitType.get(unitType);
        return handlerListFinishedWorkItems(workflowName, workflowCode, originator, startTime, endTime, instanceState,
            page, size, appCode, type, activityName, participantName, participant, sequenceNo);
    }

    @GetMapping("/list_finished_circulates")
    @ApiOperation(value = "查询已阅列表", notes = "查询已阅列表数据")
    @ApiImplicitParams({
        @ApiImplicitParam(name = "workflowName", value = "流程名称", required = false, dataType = "String",
            paramType = "query"),
        @ApiImplicitParam(name = "workflowCode", value = "流程编码", required = false, dataType = "String",
            paramType = "query"),
        @ApiImplicitParam(name = "originator", value = "发起人名称", required = false, dataType = "String",
            paramType = "query"),
        @ApiImplicitParam(name = "appCode", value = "模型编码", required = false, dataType = "String", paramType = "query"),
        @ApiImplicitParam(name = "startTime", value = "开始-接收时间", required = false, dataType = "String",
            paramType = "query"),
        @ApiImplicitParam(name = "endTime", value = "结束-接收时间", required = false, dataType = "String",
            paramType = "query"),
        @ApiImplicitParam(name = "unitType", value = "组织用户类型", required = false, dataType = "Integer",
            paramType = "query"),
        @ApiImplicitParam(name = "sequenceNo", value = "单据号", required = false, dataType = "String",
            paramType = "query"),
        @ApiImplicitParam(name = "page", value = "当前页码", required = false, dataType = "Integer", paramType = "query"),
        @ApiImplicitParam(name = "size", value = "每页显示数量", required = false, dataType = "Integer",
            paramType = "query")})
    public ResponseResult<PageVO<CirculateitemVO>> listFinishedCirculateItems(String workflowName, String workflowCode,
        String originator, String startTime, String endTime, Integer page, Integer size, String appCode,
        Integer unitType, String sequenceNo) {
        log.debug(
            "查询已阅列表：workflowName = {}, workflowCode = {}, originator = {}, startTime = {}, endTime = {}, sequenceNo = {}",
            workflowName, workflowCode, originator, startTime, endTime, sequenceNo);
        // 根据结束时间降序
        Pageable pageable = getPageable(page, size, Sort.Direction.DESC, "finishTime");
        UnitType type = unitType == null ? UnitType.USER : UnitType.get(unitType);
        Page<CirculateItemModel> circulateItemPage = listFinishedCirculateItems(getUserId(), appCode, workflowName,
            workflowCode, originator, type, startTime, endTime, pageable, sequenceNo);
        if (circulateItemPage == null) {
            return getOkResponseResult(null, "查询已阅数据为空");
        }

        List<CirculateItemModel> circulateItemModels = circulateItemPage.getContent();

        List<CirculateitemVO> circulateItemVos = model2voWithCirculate(circulateItemModels);

        Page<CirculateitemVO> circulateItemVoPage =
            new PageImpl<>(circulateItemVos, pageable, circulateItemPage.getTotalElements());
        return getOkResponseResult(new PageVO<>(circulateItemVoPage), "查询已阅数据成功");
    }

    @GetMapping("/list_my_instances")
    @ApiOperation(value = "查询我的流程列表", notes = "查询我的流程列表数据")
    @ApiImplicitParams({
        @ApiImplicitParam(name = "workflowName", value = "流程名称", required = false, dataType = "String",
            paramType = "query"),
        @ApiImplicitParam(name = "workflowCode", value = "流程编码", required = false, dataType = "String",
            paramType = "query"),
        @ApiImplicitParam(name = "startTime", value = "流程开始时间", required = false, dataType = "String",
            paramType = "query"),
        @ApiImplicitParam(name = "endTime", value = "流程结束时间", required = false, dataType = "String",
            paramType = "query"),
        @ApiImplicitParam(name = "instanceState", value = "流程实例状态", required = false, dataType = "String",
            paramType = "query"),
        @ApiImplicitParam(name = "page", value = "当前页码", required = false, dataType = "Integer", paramType = "query"),
        @ApiImplicitParam(name = "size", value = "每页显示数量", required = false, dataType = "Integer", paramType = "query"),
        @ApiImplicitParam(name = "isMobile", value = "移动端请求", required = false, dataType = "Boolean",
            paramType = "query"),
        @ApiImplicitParam(name = "squenceNo", value = "单据号", required = false, dataType = "String",
            paramType = "query"),
        @ApiImplicitParam(name = "appCode", value = "应用编码", required = false, dataType = "String",
            paramType = "query"),})
    public ResponseResult<PageVO<WorkflowInstanceVO>> listMyInstance(String workflowName, String workflowCode,
        String startTime, String endTime, String instanceState, Integer page, Integer size, Boolean isMobile,
        String sequenceNo, String appCode) {
        log.debug(
            "查询我的流程列表：workflowName = {}, workflowCode = {}, startTime = {}, endTime = {}, instanceState = {}, sequenceNo = {}",
            workflowName, workflowCode, startTime, endTime, instanceState, sequenceNo);
        Pageable pageable = PageRequest.of(page == null ? 0 : page, size == null ? 10 : size);
        Page<WorkflowInstanceModel> instancePage;
        List<WorkflowInstanceStatus> instanceStatuses = getInstanceStates(instanceState);

        if (isMobile == null) {
            isMobile = false;
        }
        Boolean mobile = isMobile;
        if (instanceStatuses == null) {
            instanceStatuses = Lists.newArrayList();
            instanceStatuses.add(WorkflowInstanceStatus.PROCESSING);
        }

        // if (instanceStatuses == null || instanceStatuses.size() == 1) {
        // WorkflowInstanceStatus instanceStateItem = instanceStatuses == null ? WorkflowInstanceStatus.PROCESSING :
        // instanceStatuses.get(0);
        // instancePage = listMyWorkflowInstanceList(getUserId(), UnitType.USER, null, workflowName, workflowCode,
        // startTime, endTime, Collections.singletonList(instanceStateItem), pageable, isMobile, squenceNo);
        // } else {
        // instancePage = listMyWorkflowInstanceList(getUserId(), UnitType.USER, null, workflowName, workflowCode,
        // startTime, endTime, instanceStatuses, pageable, isMobile, squenceNo);
        // }
        String userId = getUserId();
        instancePage = listMyWorkflowInstanceList(userId, UnitType.USER, null, workflowName, workflowCode, startTime,
            endTime, instanceStatuses, pageable, isMobile, sequenceNo, appCode);

        if (instancePage == null) {
            return getOkResponseResult(null, "查询我的流程数据为空");
        }

        List<WorkflowInstanceModel> instancePageModels = instancePage.getContent();

        List<WorkflowInstanceVO> instanceVOs = model2voWithWorkflowInstance(instancePageModels, mobile);

        for (WorkflowInstanceVO workflowInstanceVO : instanceVOs) {
            if (workflowInstanceVO.getIsTrustStart()) {
                if (userId.equals(workflowInstanceVO.getOriginator())) {
                    workflowInstanceVO.setIsTrustor(true);// 表示这条流程是当前用户委托给别人发起的
                } else if (userId.equals(workflowInstanceVO.getTrustee())) {
                    workflowInstanceVO.setIsTrustor(false);// 表示这条流程是别人委托给当前用户发起的
                }
            }
        }

        Page<WorkflowInstanceVO> instanceVOPage =
            new PageImpl<>(instanceVOs, pageable, instancePage.getTotalElements());
        log.info("我的流程数据：new PageVO<>(instanceVOPage)={}",
            JSONObject.toJSONString(new PageVO<>(instanceVOPage).toString()));
        return getOkResponseResult(new PageVO<>(instanceVOPage), "查询我的流程数据成功");
    }

    private List<WorkflowInstanceVO> model2voWithWorkflowInstance(List<WorkflowInstanceModel> instancePageModels,
        boolean isMobile) {
        Map<String, UserModel> userModelMap = null;
        if (isMobile) {
            List<String> originatorIds =
                instancePageModels.stream().map(WorkflowInstanceModel::getOriginator).collect(Collectors.toList());
            userModelMap = getOrganizationFacade().getUsers(originatorIds);
        }
        final Map<String, UserModel> originatorMap = userModelMap;
        Map<String, WorkflowTemplateHeaderModel> workflowTemplateHeaderModelMap = Maps.newHashMap();

        List<WorkflowInstanceVO> instanceVOs =
            instancePageModels.stream().filter(Objects::nonNull).map(instanceModel -> {
                WorkflowInstanceVO instanceVO = new WorkflowInstanceVO(instanceModel);

                WorkflowTemplateHeaderModel header = getAndCacheWorkflowTemplateHeaderModel(
                    workflowTemplateHeaderModelMap, instanceModel.getWorkflowCode());

                // Map<String, String> workflowNames = getWorkflowNames(instanceModel.getWorkflowCode());
                instanceVO.setName_i18n(header == null ? null : header.getName_i18n());
                instanceVO.setWorkflowName(header == null ? null : header.getWorkflowName());
                instanceVO.setSequenceNo(instanceModel.getSequenceNo());

                if (isMobile) {
                    instanceVO.setImgUrl(getUserImg(originatorMap, instanceModel.getOriginator()));
                }
                return instanceVO;
            }).collect(Collectors.toList());
        workflowTemplateHeaderModelMap.clear();
        if (userModelMap != null) {
            userModelMap.clear();
        }
        return instanceVOs;
    }

    /**
     * 根据字符串获取流程状态
     *
     * @param instanceStateStr
     *            流程实例状态字符串（以逗号拼接）
     * @return List<WorkflowInstanceStatus>
     */
    private List<WorkflowInstanceStatus> getInstanceStates(String instanceStateStr) {
        List<WorkflowInstanceStatus> statuses = null;
        if (StringUtils.isBlank(instanceStateStr)) {
            return statuses;
        }
        if (instanceStateStr.contains(",")) {
            statuses = Lists.newArrayList();
            String[] instances = instanceStateStr.split(",");
            for (String instance : instances) {
                WorkflowInstanceStatus status = null;
                try {
                    status = WorkflowInstanceStatus.valueOf(instance);
                } catch (IllegalArgumentException e) {

                }
                if (status != null) {
                    statuses.add(status);
                }
            }
            return statuses;
        } else {
            WorkflowInstanceStatus status = null;
            try {
                status = WorkflowInstanceStatus.valueOf(instanceStateStr);
            } catch (IllegalArgumentException e) {

            }
            if (status != null) {
                statuses = Lists.newArrayList();
                statuses.add(status);
            }
            return statuses;
        }
    }

    /**
     * 根据流程实例获取当前节点及参与者
     *
     * @param workflowInstanceId
     *            流程实例id
     * @param circulateResource
     *            任务对象状态
     * @return 当前节点及参与者
     */
    @ApiOperation(value = "查询我的流程列表", notes = "查询我的流程列表数据")
    @ApiImplicitParams({
        @ApiImplicitParam(name = "workflowInstanceId", value = "流程实例id", required = false, dataType = "String",
            paramType = "query"),
        @ApiImplicitParam(name = "circulateResource", required = false, dataType = "String", paramType = "query",
            value = "任务类型: WORKITEM 待办, FINISHED_WORKITEM 已办, CIRCULATE 待阅, FINISHED_CIRCULATE 已阅")})
    @GetMapping("/get_participants")
    private ResponseResult<List<Map<String, Object>>> getParticipant(String workflowInstanceId,
        CirculateResource circulateResource) {
        List<WorkItemModel> workItemModels = Lists.newArrayList();
        switch (circulateResource) {
            case WORKITEM:
                workItemModels = getWorkflowInstanceFacade().getWorkItems(workflowInstanceId, Boolean.FALSE);
                break;
            case FINISHED_WORKITEM:
                workItemModels = getWorkflowInstanceFacade().getWorkItems(workflowInstanceId, Boolean.TRUE);
                break;
            case CIRCULATE:
            case FINISHED_CIRCULATE:
            default:
                break;
        }
        List<Map<String, Object>> result = Lists.newArrayList();
        if (CollectionUtils.isEmpty(workItemModels)) {
            return getErrResponseResult(result, ErrCode.WORKFLOW_ITEM_EMPTY.getErrCode(), "当前流程没有处理人");
        }

        Map<String, List<WorkItemModel>> modelMap =
            workItemModels.stream().collect(Collectors.groupingBy(WorkItemModel::getActivityName));
        for (Map.Entry<String, List<WorkItemModel>> entry : modelMap.entrySet()) {
            Map<String, Object> dataMap = Maps.newHashMap();
            dataMap.put("activityName", entry.getKey());
            List<Map<String, String>> participants = Lists.newArrayList();
            for (WorkItemModel model : entry.getValue()) {
                Map<String, String> participantMap = Maps.newHashMap();
                participantMap.put("id", model.getParticipant());
                participantMap.put("name", model.getParticipantName());
                participants.add(participantMap);
                dataMap.put("participants", participants);
            }
            result.add(dataMap);
        }
        return getOkResponseResult(result, "流程处理人查询成功");
    }

    @ApiOperation(value = "获取代办待阅任务数量", notes = "返回结果：{\"workItemCount\": \"\", \"circulateItemCount\": \"\"}]}")
    @ApiImplicitParam(name = "appCode", value = "应用编码", required = false, dataType = "String", paramType = "query")
    @GetMapping("/get_work_count")
    public ResponseResult<Map<String, Integer>> getWorkCount(String appCode) {
        Map<String, Integer> resultMap = Maps.newHashMap();
        // 统计待办
        int workItemCount = getWorkflowInstanceFacade().countWorkItem(getUserId(), appCode);
        resultMap.put("workItemCount", workItemCount);
        // 统计待阅
        int circulateItemCount = getWorkflowInstanceFacade().countCirculateItem(getUserId(), appCode);
        resultMap.put("circulateItemCount", circulateItemCount);
        return getOkResponseResult(resultMap, "数量加载成功");
    }

    @ApiOperation(value = "加载可发起的流程",
        notes = "返回结果：{\"size\": 8,\"appCode\": \"testapp001\",\"appName\": \"测试应用1\",\"dataList\": [{\"code\": \"test_wl1\",\"name\": \"测试流程1\",\"id\": \"2c92809667f0170f0167f028209b0027\",\"icon\":\"\",\"type\":\"Folder 目录、BizModel 流程\"}]}")
    @GetMapping("/list_my_workflow")
    public ResponseResult<List<WorkflowTreeModel>> listAppPackagesAndWorkflows(boolean isMobile) {
        log.debug("加载可发起的流程");
        String userId = getUserId();
        List<WorkflowTreeModel> wfList = listAppAndWorkflow(userId, isMobile);
        return getOkResponseResult(wfList, "数据加载成功");
    }

    @ApiOperation(value = "获取可发起此流程的用户")
    @ApiImplicitParam(name = "workflowCode", value = "流程编码", required = true, dataType = "String", paramType = "query")
    @GetMapping("/list_trust_originator")
    public ResponseResult<List<TrustOriginatorVO>> listTrustOriginator(
        @RequestParam(value = "workflowCode", required = true) String workflowCode,
        @RequestParam(value = "workflowInstanceId", required = false) String workflowInstanceId) {
        Assert.hasLength(workflowCode, "流程编码不能为空");
        String userId = getUserId();
        /**
         * 有流程权限的用户
         */
        List<String> workflowPermissionOriginators = Lists.newArrayList();

        boolean childTrust = false;// 子流程委托
        if (StringUtils.isNotBlank(workflowInstanceId)) {
            WorkflowInstanceModel workflowInstanceModel =
                getWorkflowInstanceFacade().getWorkflowInstance(workflowInstanceId);
            if (workflowInstanceModel != null && StringUtils.isNotBlank(workflowInstanceModel.getParentId())
                && workflowInstanceModel.getTrustType() == WorkflowTrustType.START
                && !workflowPermissionOriginators.contains(workflowInstanceModel.getOriginator())) {
                childTrust = true;
                workflowPermissionOriginators.add(workflowInstanceModel.getOriginator());
            }
        }

        if (!childTrust) {
            Set<String> permissionTrustors = getPermissionTrustor(userId, workflowCode);
            permissionTrustors.add(userId);

            for (String id : permissionTrustors) {
                boolean checkWorkflowPermission =
                    getAppManagementFacade().checkWorkflowPermissionByUserIdAndWorkflowCode(id, workflowCode);
                if (checkWorkflowPermission) {
                    workflowPermissionOriginators.add(id);
                }
            }
        }

        List<TrustOriginatorVO> result = Lists.newArrayList();
        Map<String, UserModel> userModelMap = getOrganizationFacade().getUsers(workflowPermissionOriginators);
        if (userModelMap == null || userModelMap.isEmpty()) {
            log.warn("用户 {} 没有流程 {} 的发起权限", userId, workflowCode);
            return getOkResponseResult(result, "获取流程发起人成功");
        }

        for (UserModel userModel : userModelMap.values()) {
            TrustOriginatorVO trustOriginatorVO = new TrustOriginatorVO();
            trustOriginatorVO.setId(userModel.getId());
            trustOriginatorVO.setName(userModel.getName());
            trustOriginatorVO.setImgUrl(userModel.getImgUrl());
            trustOriginatorVO.setDepartments(getUserDepartments(userModel.getId()));
            trustOriginatorVO.setIsTrustor(!userId.equals(userModel.getId()));

            result.add(trustOriginatorVO);
        }

        return getOkResponseResult(result, "获取流程发起人成功");
    }

    /**
     * 获取有权限发起流程的委托人
     *
     * @param userId
     * @param workflowCode
     * @return
     */
    @SuppressWarnings("deprecation")
    private Set<String> getPermissionTrustor(String userId, String workflowCode) {
        Date now = new Date();
        WorkflowTrustRuleModel param = new WorkflowTrustRuleModel();
        param.setTrustee(userId);
        param.setStartTime(now);
        param.setEndTime(now);
        param.setTrustType(TrustType.START);
        param.setState(WorkflowTrustRuleState.RUN);

        Pageable pageable = new PageRequest(0, 100);
        Page<WorkflowTrustRuleModel> ruleModelPage =
            getWorkflowInstanceFacade().queryFullWorkflowTrust(param, pageable);
        Set<String> permissionTrustors = Sets.newHashSet();
        if (ruleModelPage != null && CollectionUtils.isNotEmpty(ruleModelPage.getContent())) {
            for (WorkflowTrustRuleModel rule : ruleModelPage) {
                if (rule.getTrustType() != TrustType.START)
                    continue;
                if (rule.getRangeType() == RangeType.ALL) {
                    permissionTrustors.add(rule.getTrustor());
                } else {
                    List<WorkflowTrustModel> workflowTrusts = rule.getWorkflowTrusts();
                    if (CollectionUtils.isEmpty(workflowTrusts)) {
                        continue;
                    }
                    boolean permissionWorkflow =
                        workflowTrusts.stream().anyMatch(workflowTrustModel -> Objects.nonNull(workflowTrustModel)
                            && workflowCode.equals(workflowTrustModel.getWorkflowCode()));
                    if (permissionWorkflow) {
                        permissionTrustors.add(rule.getTrustor());
                    }
                }
            }
        }
        return permissionTrustors;
    }

    @ApiOperation(value = "加载指定用户可发起的流程",
        notes = "返回结果：{\"size\": 8,\"appCode\": \"testapp001\",\"appName\": \"测试应用1\",\"dataList\": [{\"code\": \"test_wl1\",\"name\": \"测试流程1\",\"id\": \"2c92809667f0170f0167f028209b0027\",\"icon\":\"\",\"type\":\"Folder 目录、BizModel 流程\"}]}")
    @GetMapping("/list_permission_workflow")
    public ResponseResult<List<WorkflowTreeModel>> listPermissionWorkflows(
        @RequestParam(value = "userId", required = true) String userId,
        @RequestParam(value = "isMobile", defaultValue = "false", required = false) boolean isMobile) {
        log.debug("加载指定用户可发起的流程");
        List<WorkflowTreeModel> wfList = listAppAndWorkflow(userId, isMobile);
        return getOkResponseResult(wfList, "数据加载成功");
    }

    private List<WorkflowTreeModel> listAppAndWorkflow(String userId, boolean isMobile) {
        List<WorkflowTreeModel> wfList = null;
        if (isMobile) {
            wfList = redisCacheService.getUserMWorkflows(userId);
        } else {
            wfList = redisCacheService.getUserPCWorkflows(userId);
        }
        if (wfList != null) {
            return wfList;
        }

        wfList = getAppManagementFacade().getOperationalWorkflow(userId, isMobile);
        if (isMobile) {
            redisCacheService.putUserMWorkflows(userId, wfList);
        } else {
            redisCacheService.putUserPCWorkflows(userId, wfList);
        }
        return wfList;
    }

    @ApiOperation(value = "根据目录id加载目录下可发起的流程",
        notes = "根据目录id加载目录下可发起的流程。返回结果：{\"size\": 8,\"appCode\": \"testapp001\",\"appName\": \"测试应用1\",\"dataList\": [{\"code\": \"test_wl1\",\"name\": \"测试流程1\",\"id\": \"2c92809667f0170f0167f028209b0027\",\"icon\":\"\",\"type\":\"Folder 目录、BizModel 流程\"}]}")
    @ApiImplicitParams({
        @ApiImplicitParam(name = "id", value = "目录id", required = true, dataType = "String", paramType = "query"),
        @ApiImplicitParam(name = "page", value = "当前页码", required = false, dataType = "Integer", paramType = "query"),
        @ApiImplicitParam(name = "size", value = "每页显示数量", required = false, dataType = "Integer",
            paramType = "query")})
    @GetMapping("/list_my_workflow_by_folder_id")
    public ResponseResult<List<WorkflowTreeModel.Node>> listWorkflowsByFolderId(String id, Boolean isMobile) {
        log.debug("根据目录id加载目录下可发起的流程: id = {}", id);
        validateNotEmpty(id, "目录id为空");
        if (isMobile == null) {
            isMobile = false;
        }
        List<WorkflowTreeModel.Node> templateList =
            getAppManagementFacade().getWorkflowsByFolderId(getUserId(), id, isMobile);
        return getOkResponseResult(templateList, "流程加载成功");
    }

    @ApiOperation(value = "根据流程名称查询流程",
        notes = "根据流程名称查询流程。返回结果：{\"size\": 8,\"dataList\": [{\"code\": \"test_wl1\",\"name\": \"测试流程1\",\"id\": \"2c92809667f0170f0167f028209b0027\",\"icon\":\"\",\"type\":\"Folder 目录、BizModel 流程\"}]}")
    @ApiImplicitParams({
        @ApiImplicitParam(name = "workflowName", value = "流程名称", required = true, dataType = "String",
            paramType = "query"),
        @ApiImplicitParam(name = "language", value = "语言  zh 中文, en 英文", required = true, dataType = "String",
            paramType = "query")})
    @GetMapping("/list_my_workflow_by_name")
    public ResponseResult<WorkflowTreeModel> listWorkflowsByWorkflowName(String workflowName, String language,
        boolean isMobile) {
        log.debug("根据流程名称查询流程: workflowName = {}", workflowName);

        WorkflowTreeModel appModel =
            getAppManagementFacade().searchWorkflow(getUserId(), workflowName, isMobile, language);
        return getOkResponseResult(appModel, "流程加载成功");
    }

    @ApiOperation(value = "根据流程名称查询流程,返回树形结构")
    @ApiImplicitParams({
        @ApiImplicitParam(name = "workflowName", value = "流程名称", required = true, dataType = "String",
            paramType = "query"),
        @ApiImplicitParam(name = "language", value = "语言  zh 中文, en 英文", required = true, dataType = "String",
            paramType = "query")})
    @GetMapping("/search_workflow_by_name")
    public ResponseResult<List<AppPackageVO>> searchWorkflowsByWorkflowName(String workflowName, String language,
        boolean isMobile) {
        log.debug("根据流程名称查询流程: workflowName = {}", workflowName);
        List<AppPackageVO> result = new ArrayList<>();
        List<WorkflowTemplateHeaderModel> workflowTemplateHeaderModels =
            getAppManagementFacade().searchWorkflowTreeByName(getUserId(), workflowName, isMobile, language);
        Map<String, List<WorkflowTemplateHeaderModel>> modelMap = workflowTemplateHeaderModels.stream()
            .collect(Collectors.groupingBy(WorkflowTemplateHeaderModel::getSchemaCode));
        List<AppFunctionModel> appFunctionModels = new ArrayList<>();
        for (Map.Entry<String, List<WorkflowTemplateHeaderModel>> entry : modelMap.entrySet()) {
            List<WorkflowTemplateHeaderModel> appModels = entry.getValue();
            String schemaCode = appModels.get(0).getSchemaCode();
            AppFunctionModel appFunctionModel = getAppManagementFacade().getAppFunctionByCode(schemaCode);
            AppFunctionVO vo = new AppFunctionVO();
            BeanUtils.copyProperties(appFunctionModel, vo);
            appModels.forEach(appModel -> vo.addWorkflows(appModel));
            appFunctionModels.add(vo);
        }

        Map<String, AppFunctionModel> allModel = new LinkedHashMap<>(appFunctionModels.size() * 2);
        for (AppFunctionModel appFunctionModel : appFunctionModels) {
            if (allModel.containsKey(appFunctionModel.getParentId())) {
                allModel.get(appFunctionModel.getParentId()).addChildren(appFunctionModel);
            } else {
                AppFunctionModel temp = appFunctionModel;
                while (true) {
                    AppFunctionModel appFunction = getAppManagementFacade().getAppFunction(temp.getParentId());
                    if (appFunction == null) {
                        if (!allModel.containsKey(temp.getId())) {
                            allModel.put(temp.getId(), temp);
                        }
                        break;
                    }
                    if (allModel.containsKey(appFunction.getId())) {
                        temp = allModel.get(appFunction.getId());
                    } else {
                        AppFunctionModel folderModel = new AppFunctionModel();
                        BeanUtils.copyProperties(appFunction, folderModel);
                        folderModel.addChildren(temp);
                        allModel.put(appFunction.getId(), folderModel);
                        temp = folderModel;
                    }
                }
            }
        }
        List<AppFunctionModel> baseResult = new LinkedList<>();
        allModel.values().forEach(item -> {
            AppPackageModel appPackage = getAppManagementFacade().getAppPackage(item.getParentId());
            if (appPackage == null) {
                return;
            }
            baseResult.add(item);
        });
        baseResult.sort(Comparator.comparing(AppFunctionModel::getSortKey));

        Map<String, AppPackageVO> map = new HashMap<>();
        baseResult.forEach(model -> {
            AppPackageVO vo = map.get(model.getParentId());
            if (vo == null) {
                AppPackageModel appPackage = getAppManagementFacade().getAppPackage(model.getParentId());
                if (appPackage == null) {
                    return;
                }
                vo = new AppPackageVO();
                BeanUtils.copyProperties(appPackage, vo);
            }
            vo.addChildren(model);
            map.put(vo.getId(), vo);
        });
        map.forEach((Key, value) -> result.add(value));
        result.sort(Comparator.comparing(AppPackageVO::getSortKey));
        return getOkResponseResult(result, "成功获取流程的树结构列表");
    }

    @ApiOperation(value = "获取流程实例状态列表接口", notes = "获取流程实例状态列表接口")
    @GetMapping("/list_instance_status")
    public ResponseResult<List<Map<String, Object>>> listWorkflowInstanceStatus() {
        List<Map<String, Object>> statusList = Lists.newArrayList();
        for (WorkflowInstanceStatus status : WorkflowInstanceStatus.values()) {
            Map<String, Object> map = Maps.newHashMap();
            map.put("index", status.getIndex());
            map.put("name", status.getName());
            statusList.add(map);
        }
        return getOkResponseResult(statusList, "成功调用获取流程实例状态列表接口");
    }

    @ApiOperation(value = "根据流程实例id获取流程实例的基本信息", notes = "根据流程实例id获取流程实例的基本信息")
    @ApiImplicitParam(name = "workflowInstanceId", value = "流程实例id", required = true, dataType = "String",
        paramType = "query")
    @GetMapping("/get_instance_baseinfo")
    public ResponseResult<WorkflowIntanceBaseInfoVO>
        getWorkflowIntanceBaseInfoById(@NotBlank(message = "流程实例id不允许为空") String workflowInstanceId) {
        return this.handleWorkflowIntanceBaseInfo(workflowInstanceId);
    }

    @ApiOperation(value = "根据流程实例id获取流程图", notes = "根据流程实例id获取流程图")
    @ApiImplicitParam(name = "workflowInstanceId", value = "流程实例id", required = true, dataType = "String",
        paramType = "query")
    @GetMapping("/get_workflowtemplate_chart")
    public ResponseResult<WorkflowTemplateChartVO>
        getWorkflowTemplateChartById(@NotBlank(message = "流程实例id不允许为空") String workflowInstanceId) {
        // WorkflowInstanceOptions workflowInstanceOptions = WorkflowInstanceOptions.builder()
        // .setWorkflowInstanceId(workflowInstanceId)
        // .setAppManagementFacade(getAppManagementFacade())
        // .setWorkflowInstanceFacade(getWorkflowInstanceFacade())
        // .setBizObjectFacade(getBizObjectFacade()).build();

        WorkflowInstanceModel instance = getWorkflowInstanceFacade().getWorkflowInstance(workflowInstanceId);
        if (instance == null) {
            throw new PortalException(ErrCode.WORKFLOW_INS_EMPTY);
        }
        WorkflowTemplateModel templateModel = getAppManagementFacade()
            .getSimplePublishedWorkflowTemplate(instance.getWorkflowCode(), instance.getWorkflowVersion());
        if (templateModel == null) {
            throw new PortalException(ErrCode.WORKFLOW_NOT_EXIST);
        }
        List<WorkflowTokenModel> workflowTokens = getWorkflowInstanceFacade().getWorkflowTokens(workflowInstanceId);
        List<Activity> activities = templateModel.getActivities();

        // List<WorkflowTokenModel> workflowTokens = workflowInstanceOptions.getWorkflowTokens();
        // List<WorkflowInstanceModel> subInstances = workflowInstanceOptions.getSubInstances();
        // WorkflowTemplateModel templateModel = workflowInstanceOptions.getTemplate();
        // List<Activity> activities = workflowInstanceOptions.getActivities();

        // 组装map, key为流程节点code，value为节点状态
        Map<String, WorkflowTokenStatus> activityMap = new HashMap<>();
        // tokenId升序
        if (!CollectionUtils.isEmpty(workflowTokens)) {
            for (WorkflowTokenModel workflowToken : workflowTokens) {
                activityMap.put(workflowToken.getActivityCode(), workflowToken.getState());
            }
        }
        if (log.isDebugEnabled()) {
            log.debug("activityMap = [{}]", JSON.toJSONString(activityMap));
        }

        List<WorkflowInstanceModel> subInstances = null;
        boolean isQuerySubInstance = activities.stream().anyMatch(
            activity -> activity instanceof SubInstanceActivity && activityMap.containsKey(activity.getActivityCode()));
        if (isQuerySubInstance) {
            subInstances = getWorkflowInstanceFacade().getWorkflowInstanceByParentId(workflowInstanceId);
        }

        if (log.isDebugEnabled()) {
            log.debug("workflowTokens = [{}]", JSON.toJSONString(workflowTokens));
            log.trace("subInstances = [{}]", JSON.toJSONString(subInstances));
            log.trace("activities = [{}]", JSON.toJSONString(activities));
        }

        List<ActivityStatusVO> activityCodeStatus = Lists.newArrayList();
        // 设置流程节点状态
        if (CollectionUtils.isNotEmpty(activities)) {
            for (Activity activity : activities) {
                if (log.isDebugEnabled()) {
                    log.debug("activity code = [{}]", activity.getActivityCode());
                }

                if (activity instanceof SubInstanceActivity && activityMap.containsKey(activity.getActivityCode())) {
                    activityCodeStatus.add(WorkflowInstanceStatusHelper
                        .disposeSubInstanceActivityStatus((SubInstanceActivity)activity, subInstances));
                } else if (activityMap.containsKey(activity.getActivityCode())) {
                    ActivityStatusVO status = new ActivityStatusVO();
                    status.setActivityCode(activity.getActivityCode());
                    WorkflowTokenStatus tokenStatus = activityMap.get(activity.getActivityCode());
                    ActivityStatus activityStatus = null;
                    if (WorkflowTokenStatus.FINISHED.equals(tokenStatus)
                        || WorkflowTokenStatus.CANCELLED.equals(tokenStatus)
                        || WorkflowTokenStatus.REVOKE.equals(tokenStatus)) {
                        activityStatus = ActivityStatus.EXECUTED_NODE;
                    } else if (WorkflowTokenStatus.UNFINISHED.equals(tokenStatus)) {
                        activityStatus = ActivityStatus.PROCESSING_NODE;
                    } else if (WorkflowTokenStatus.EXCEPTION.equals(tokenStatus)) {
                        activityStatus = ActivityStatus.ABNORMAL_NODE;
                    }
                    status.setStatus(activityStatus);
                    activityCodeStatus.add(status);
                }
            }
        }

        if (log.isDebugEnabled()) {
            log.debug("activityCodeStatus = [{}]", JSON.toJSONString(activityCodeStatus));
        }

        WorkflowTemplateChartVO workflowChart = new WorkflowTemplateChartVO(templateModel);
        workflowChart.setActivityStatus(activityCodeStatus);
        return getOkResponseResult(workflowChart, "成功调用获取流程图接口");
    }

    @ApiOperation(value = "根据流程实例id获取流程节点预估处理人", notes = "根据流程实例id获取流程节点预估处理人")
    @ApiImplicitParams({
        @ApiImplicitParam(name = "workflowInstanceId", value = "流程实例id", required = true, dataType = "String",
            paramType = "query"),
        @ApiImplicitParam(name = "activityCode", value = "节点编码", required = true, dataType = "String",
            paramType = "query")})
    @GetMapping("/get_activity_preprocessors")
    public ResponseResult<List<ActivityPreprocessorVO>>
        getActivityPreprocessors(@NotBlank(message = "流程实例id不允许为空") String workflowInstanceId, String activityCode) {
        // 获取预处理用户列表
        // Set<String> userIdList = getWorkflowInstanceFacade().getParticipants(workflowInstanceId, activityCode);
        List<ActivityPreprocessorVO> preprocessorVOList = new ArrayList<>();

        ParticipantModel participantModel =
            getWorkflowInstanceFacade().getFullParticipants(workflowInstanceId, activityCode);

        if (participantModel == null || CollectionUtils.isEmpty(participantModel.getTodoParticipants())) {
            // 当前参与者不可预测
            return getOkResponseResult(preprocessorVOList, "成功调用获取预处理接口");
        }
        Set<String> todoParticipants = participantModel.getTodoParticipants();
        LinkedHashMap<String, String> trustParticipantMap = participantModel.getTrustParticipantMap();
        // Set<String> trustorSet = trustParticipantMap == null ? Sets.newHashSet() : trustParticipantMap.keySet();//委托人
        Collection<String> trusteeSet = trustParticipantMap == null ? Sets.newHashSet() : trustParticipantMap.values();// 被委托人

        List<String> userIds = Lists.newArrayList(todoParticipants);
        userIds.addAll(trusteeSet);

        Map<String, UserModel> userModelMap = getOrganizationFacade().getUsers(userIds);
        if (userModelMap == null || userModelMap.isEmpty()) {
            return getOkResponseResult(preprocessorVOList, "成功调用获取预处理接口");
        }

        List<String> deptIdList = userModelMap.values().stream()
            .filter(userModel -> Objects.nonNull(userModel) && StringUtils.isNotBlank(userModel.getDepartmentId()))
            .map(UserModel::getDepartmentId).collect(Collectors.toList());
        // 拼接部门树
        if (CollectionUtils.isEmpty(deptIdList)) {
            return getOkResponseResult(preprocessorVOList, "成功调用获取预处理接口");
        }
        Map<String, List<DepartmentModel>> departmentMap =
            getOrganizationFacade().getDepartmentsByParentIds(deptIdList);
        if (departmentMap == null || departmentMap.isEmpty()) {
            return getOkResponseResult(preprocessorVOList, "成功调用获取预处理接口");
        }

        for (String userId : todoParticipants) {
            UserModel userModel = userModelMap.get(userId);
            if (userModel == null) {
                continue;
            }
            List<DepartmentModel> parentDepartments = departmentMap.get(userModel.getDepartmentId());
            if (CollectionUtils.isEmpty(parentDepartments)) {
                continue;
            }
            ActivityPreprocessorVO preprocessorVO = new ActivityPreprocessorVO();
            List<String> parentDepartmentName =
                parentDepartments.stream().sorted(Comparator.comparing(DepartmentModel::getQueryCode))
                    .map(DepartmentModel::getName).collect(Collectors.toList());

            // 被委托人
            String trustee = null == trustParticipantMap ? null : trustParticipantMap.get(userId);
            UserModel trusteeUserModel;
            List<DepartmentModel> trusteeParentDepartments;
            if (StringUtils.isNotBlank(trustee) && !trustee.equals(userId)
                && (trusteeUserModel = userModelMap.get(trustee)) != null
                && (trusteeParentDepartments = departmentMap.get(trusteeUserModel.getDepartmentId())) != null
                && !trusteeParentDepartments.isEmpty()) {// 委托其他人处理
                final String trustor = userId;// 委托人

                List<String> trusteeParentDepartmentName =
                    trusteeParentDepartments.stream().sorted(Comparator.comparing(DepartmentModel::getQueryCode))
                        .map(DepartmentModel::getName).collect(Collectors.toList());

                // 被委托人
                preprocessorVO.setId(trustee);
                preprocessorVO.setName(trusteeUserModel.getName());
                preprocessorVO.setImgUrl(trusteeUserModel.getImgUrl());
                preprocessorVO.setDepartmentNames(trusteeParentDepartmentName);

                ActivityPreprocessorVO trustorProcessor = new ActivityPreprocessorVO();
                // 被委托人
                trustorProcessor.setId(trustor);
                trustorProcessor.setName(userModel.getName());
                trustorProcessor.setImgUrl(userModel.getImgUrl());
                trustorProcessor.setDepartmentNames(parentDepartmentName);
                preprocessorVO.setTrustorProcessor(trustorProcessor);
                preprocessorVO.setIsTrust(true);
            } else {
                preprocessorVO.setId(userId);
                preprocessorVO.setName(userModel.getName());
                preprocessorVO.setImgUrl(userModel.getImgUrl());
                preprocessorVO.setDepartmentNames(parentDepartmentName);
            }
            preprocessorVOList.add(preprocessorVO);
        }
        return getOkResponseResult(preprocessorVOList, "成功调用获取预处理接口");
    }

    @ApiOperation(value = "根据流程实例id获取流程日志", notes = "根据流程实例id获取流程图")
    @ApiImplicitParam(name = "workflowInstanceId", value = "流程实例id", required = true, dataType = "String",
        paramType = "query")
    @GetMapping("/list_instance_logs")
    public ResponseResult<List<WorkflowIntanceLogInfoVO>> getWorkflowInstanceLogInfoById(String workflowInstanceId) {
        List<WorkflowIntanceLogInfoVO> logInfoList = Lists.newArrayList();
        if (StringUtils.isBlank(workflowInstanceId)) {
            log.error("workflowInstance is blank");
            return getOkResponseResult(logInfoList, "成功调用获取流程日志接口");
        }
        WorkflowInstanceOptions workflowInstanceOptions =
            WorkflowInstanceOptions.builder().setWorkflowInstanceId(workflowInstanceId).setQueryCirculate(true)
                .setDubboConfigService(getDubboConfigService()).build();
        List<WorkflowTokenModel> workflowTokens = workflowInstanceOptions.getWorkflowTokens();
        if (CollectionUtils.isEmpty(workflowTokens)) {
            return getOkResponseResult(logInfoList, "成功调用获取流程日志接口");
        }
        List<WorkflowInstanceModel> subInstances = workflowInstanceOptions.getSubInstances();

        WorkflowInstanceHelper workflowInstanceHelper = new WorkflowInstanceHelper(this.getWorkflowInstanceFacade(),
            this.getAppManagementFacade(), this.getBizObjectFacade(), this.getOrganizationFacade());

        List<WorkItemAllVO> allWorkItems = Lists.newArrayList();
        // 待办已办待阅已阅，组装map
        Map<String, List<WorkItemAllVO>> workItemMap =
            workflowInstanceHelper.getWorkItemsMap(workflowInstanceOptions, allWorkItems);
        if (log.isDebugEnabled()) {
            log.debug("组装map, workItemMap = {}", JSONObject.toJSONString(workItemMap));
            log.debug("workflowTokens = {}", JSONObject.toJSONString(workflowTokens));
        }

        List<String> participants = Lists.newArrayList();
        if (!CollectionUtils.isEmpty(allWorkItems)) {
            for (WorkItemAllVO workItemAllVO : allWorkItems) {
                if (StringUtils.isNotBlank(workItemAllVO.getParticipant())) {
                    participants.add(workItemAllVO.getParticipant());
                }
                if (StringUtils.isNotBlank(workItemAllVO.getTrustor())) {
                    participants.add(workItemAllVO.getTrustor());
                }
            }
        }

        Map<String, UserModel> workItemsAllParticipants =
            workflowInstanceHelper.getParticipantMapByUserIds(participants);

        List<BizCommentModel> bizComments = workflowInstanceOptions.getBizComments();
        for (WorkflowTokenModel workflowToken : workflowTokens) {
            Activity activity = workflowInstanceOptions.getActivity(workflowToken.getActivityCode());
            Boolean isStart = workflowInstanceOptions.isStartActivity(activity);
            if (activity instanceof SubInstanceActivity) {
                workflowInstanceHelper.disposeSubInstance(subInstances, workflowToken, workItemMap, logInfoList,
                    activity);
            } else if (activity instanceof SystemActivity) {
                workflowInstanceHelper.disposeSystemActivity(workflowToken, workItemMap, activity, logInfoList);
            } else if (activity instanceof CirculateActivity) {
                workflowInstanceHelper.disposeCirculateActivity(workflowToken, workItemMap, activity, logInfoList,
                    isStart, workItemsAllParticipants);
            } else {
                workflowInstanceHelper.disposeParticipantInstance(workflowToken, workItemMap, activity, logInfoList,
                    isStart, bizComments, workItemsAllParticipants);
            }
        }
        return getOkResponseResult(logInfoList, "成功调用获取流程日志接口");
    }

    @ApiOperation(value = "根据流程实例id获取审批记录列表", notes = "根据流程实例id获取审批记录列表")
    @ApiImplicitParam(name = "workflowInstanceId", value = "流程实例id", required = true, dataType = "String",
        paramType = "query")
    @GetMapping("/list_workitem_approvals")
    public ResponseResult<List<WorkItemApprovalVO>>
        getWorkItemApprovalsById(@NotBlank(message = "流程实例id不允许为空") String workflowInstanceId) {
        return handleWorkItemApprovals(workflowInstanceId);
    }

    @SuppressWarnings("unchecked")
    @ApiOperation(value = "查询流程实例列表", notes = "查询流程实例列表")
    @ApiImplicitParams({
        @ApiImplicitParam(name = "workflowName", value = "流程名称", dataType = "String", paramType = "query"),
        @ApiImplicitParam(name = "workflowCode", value = "流程编码", dataType = "String", paramType = "query"),
        @ApiImplicitParam(name = "startTime", value = "流程开始时间", dataType = "String", paramType = "query"),
        @ApiImplicitParam(name = "endTime", value = "流程结束时间", dataType = "String", paramType = "query"),
        @ApiImplicitParam(name = "instanceState", value = "流程实例状态", dataType = "String", paramType = "query"),
        @ApiImplicitParam(name = "page", value = "当前页码", dataType = "Integer", paramType = "query"),
        @ApiImplicitParam(name = "size", value = "每页显示数量", dataType = "Integer", paramType = "query"),
        @ApiImplicitParam(name = "unitType", value = "发起人类型", dataType = "Integer", paramType = "query"),
        @ApiImplicitParam(name = "originator", value = "发起人", dataType = "String", paramType = "query"),
        @ApiImplicitParam(name = "simulative", value = "是否模拟流程", dataType = "Boolean", paramType = "query")})
    @GetMapping("/list_instances")
    public ResponseResult<PageVO<WorkflowInstanceVO>> listWorkflowIntances(
        @RequestParam(required = false) String workflowName, @RequestParam(required = false) String workflowCode,
        @RequestParam(required = false) String startTime, @RequestParam(required = false) String endTime,
        @RequestParam(required = false) WorkflowInstanceStatus instanceState,
        @RequestParam(required = false) Integer page, @RequestParam(required = false) Integer size,
        @RequestParam(required = false) Integer unitType, @RequestParam(required = false) String originator,
        @RequestParam(required = false) String sequenceNo,
        @RequestParam(required = false, defaultValue = "false") Boolean simulative) {
        if (log.isDebugEnabled()) {
            log.debug(
                "查询流程实例列表：workflowName = {}, workflowCode = {}, startTime = {}, endTime = {}, instanceState = {}, originator = {}",
                workflowName, workflowCode, startTime, endTime, instanceState, originator);
        }
        Pageable pageable = PageRequest.of(page == null ? 0 : page, size == null ? 10 : size);
        UnitType type = unitType == null ? UnitType.USER : UnitType.get(unitType);
        Page<WorkflowInstanceModel> instancePage =
            listWorkflowInstances(originator, type, this.getUserId(), workflowName, workflowCode, startTime, endTime,
                instanceState == null ? Collections.EMPTY_LIST : Collections.singletonList(instanceState), pageable,
                sequenceNo, simulative);
        if (instancePage == null) {
            return getOkResponseResult(null, "成功查询流程实例列表");
        }
        List<WorkflowInstanceModel> instancePageModels = instancePage.getContent();

        // List<WorkflowInstanceVO> instances = instancePageModels.stream().filter(Objects::nonNull).map(instanceModel
        // -> {
        // WorkflowInstanceVO instance = new WorkflowInstanceVO(instanceModel);
        // instance.setImgUrl(getUserImg(instanceModel.getOriginator()));
        // WorkflowTemplateHeaderModel header =
        // getAppManagementFacade().getWorkflowTemplateHeader(instanceModel.getWorkflowCode());
        // instance.setWorkflowName(header == null ? StringUtils.EMPTY : header.getWorkflowName());
        // instance.setName_i18n(header == null ? StringUtils.EMPTY : header.getName_i18n());
        // return instance;
        // }).collect(Collectors.toList());

        List<WorkflowInstanceVO> instanceVOs = model2voWithWorkflowInstance(instancePageModels, true);

        Page<WorkflowInstanceVO> instanceVOPage =
            new PageImpl<>(instanceVOs, pageable, instancePage.getTotalElements());
        return getOkResponseResult(new PageVO<>(instanceVOPage), "成功查询流程实例列表");
    }

    @ApiOperation(value = "查询任务列表", notes = "查询任务列表")
    @ApiImplicitParams({
        @ApiImplicitParam(name = "workflowName", value = "流程名称", dataType = "String", paramType = "query"),
        @ApiImplicitParam(name = "workflowCode", value = "流程编码", dataType = "String", paramType = "query"),
        @ApiImplicitParam(name = "originator", value = "发起人", dataType = "String", paramType = "query"),
        @ApiImplicitParam(name = "startTime", value = "开始-任务处理时间", dataType = "String", paramType = "query"),
        @ApiImplicitParam(name = "endTime", value = "结束-任务处理时间", dataType = "String", paramType = "query"),
        @ApiImplicitParam(name = "status",
            value = "任务状态,未启动:NOTSTARTED,进行中:INPROGRESS,已完成:FINISHED,被取消:CANCELLED,草稿:DRAFT", dataType = "String",
            paramType = "query"),
        @ApiImplicitParam(name = "unitType", value = "发起人组织类型", dataType = "Integer", paramType = "query"),
        @ApiImplicitParam(name = "page", value = "当前页码", dataType = "Integer", paramType = "query"),
        @ApiImplicitParam(name = "size", value = "每页显示数量", dataType = "Integer", paramType = "query"),
        @ApiImplicitParam(name = "sequenceNo", value = "单据号", dataType = "String", paramType = "query")})
    @GetMapping("/list_workitems")
    public ResponseResult<PageVO<WorkItemVO>> listWorkItems(@RequestParam(required = false) String workflowName,
        @RequestParam(required = false) String workflowCode, @RequestParam(required = false) String originator,
        @RequestParam(required = false) String startTime, @RequestParam(required = false) String endTime,
        @RequestParam(required = false) String status, @RequestParam(required = false) Integer unitType,
        @RequestParam(required = false) Integer page, @RequestParam(required = false) Integer size,
        @RequestParam(required = false) String activityName, @RequestParam(required = false) String participantName,
        @RequestParam(required = false) String participant, @RequestParam(required = false) String sequenceNo) {
        if (log.isDebugEnabled()) {
            log.debug(
                "查询任务列表：workflowName = {}, workflowCode = {}, originator = {},unitType={}, startTime = {}, endTime = {}, status = {}, sequenceNo = {}",
                workflowName, workflowCode, originator, unitType, startTime, endTime, status, sequenceNo);
        }
        UnitType type = unitType == null ? UnitType.USER : UnitType.get(unitType);

        if (StringUtils.isEmpty(status)) {
            throw new PortalException(ErrCode.WORKFLOW_INS_STATE_NOT_EXIST);
        }
        // boolean isMobile = isMobile();
        WorkItemStatus workItemStatus = WorkItemStatus.valueOf(status);
        Sort sort;
        Pageable pageable;
        Page<WorkItemModel> workItemsPage;
        List<WorkItemVO> workItems;

        switch (workItemStatus) {
            case DRAFT:
            case NOTSTARTED:
            case INPROGRESS:// 处理中
                // 接收时间倒序
                sort = new Sort(Sort.Direction.DESC, "startTime");
                pageable = PageRequest.of(page == null ? 0 : page, size == null ? 10 : size, sort);
                workItemsPage =
                    getWorkflowInstanceFacade().queryWorkItems(StringUtils.EMPTY, workflowName, workflowCode,
                        originator, type, startTime, endTime, null, workItemStatus, null, pageable, sequenceNo);
                workItems = getPageFromWorkItems(workItemsPage);
                break;
            case CANCELLED:
            case FINISHED:
                // 已完成 被取消 完成时间倒序
                sort = new Sort(Sort.Direction.DESC, "finishTime");
                pageable = PageRequest.of(page == null ? 0 : page, size == null ? 10 : size, sort);
                workItemsPage = getWorkflowInstanceFacade().queryFinishedWorkItems(StringUtils.EMPTY, StringUtils.EMPTY,
                    workflowName, workflowCode, originator, type, startTime, endTime, null, workItemStatus, pageable,
                    activityName, participantName, participant, sequenceNo);
                // workItems = workItemsPage.getContent().stream().filter(Objects::nonNull).map(workItemModel -> {
                // WorkItemVO workItemVO = new WorkItemVO(workItemModel);
                // WorkflowTemplateHeaderModel header =
                // getAppManagementFacade().getWorkflowTemplateHeader(workItemModel.getWorkflowCode());
                // workItemVO.setWorkflowName(header == null ? StringUtils.EMPTY : header.getWorkflowName());
                //
                // if (isMobile) {
                // workItemVO.setImgUrl(getUserImg(workItemModel.getOriginator()));
                // }
                //
                // return workItemVO;
                // }).collect(Collectors.toList());

                workItems = getPageFromWorkItems(workItemsPage);
                break;
            default:
                throw new PortalException(ErrCode.WORKFLOW_INS_STATE_ILLEGAL.getErrCode(),
                    ErrCode.WORKFLOW_INS_STATE_ILLEGAL.getErrMsg());
        }

        Page<WorkItemVO> workItemVOPage = new PageImpl<>(workItems, pageable, workItemsPage.getTotalElements());
        return getOkResponseResult(new PageVO<>(workItemVOPage), "成功查询任务列表");
    }

    // protected List<WorkItemVO> getPageFromWorkItems(Page<WorkItemModel> workItemsPage) {
    // return workItemsPage.getContent().stream().filter(Objects::nonNull).map(workItemModel -> {
    // WorkItemVO workItemVO = new WorkItemVO(workItemModel);
    // WorkflowTemplateHeaderModel header =
    // getAppManagementFacade().getWorkflowTemplateHeader(workItemModel.getWorkflowCode());
    // workItemVO.setWorkflowName(header == null ? StringUtils.EMPTY : header.getWorkflowName());
    // workItemVO.setName_i18n(header == null ? StringUtils.EMPTY : header.getName_i18n());
    //
    // if (isMobile()) {
    // workItemVO.setImgUrl(getUserImg(workItemModel.getOriginator()));
    // }
    // return workItemVO;
    // }).collect(Collectors.toList());
    // }

    @ApiOperation(value = "根据流程实例id获取驳回页面的流程节点列表", notes = "根据流程实例id获取驳回页面的流程节点列表")
    @ApiImplicitParam(name = "workflowInstanceId", value = "流程实例id", required = true, dataType = "String",
        paramType = "query")
    @GetMapping("/list_reject_activities")
    public ResponseResult<List<ActivityStatusVO>> listRejectActivities(
        @NotBlank(message = "流程实例id不允许为空") @RequestParam String workflowInstanceId,
        @NotBlank(message = "节点编码不允许为空") @RequestParam String activityCode) {
        return this.handleRejectActivities(workflowInstanceId, activityCode);
    }

    @ApiOperation(value = "流程操作日志", notes = "流程操作日志")
    @GetMapping("/list_operation")
    @ApiImplicitParam(name = "workflowInstanceId", value = "流程实例id", required = true, dataType = "String",
        paramType = "query")
    public ResponseResult<List<BizObjectLogModel>> listOperationLogs(@RequestParam String workflowInstanceId) {
        if (Strings.isNullOrEmpty(workflowInstanceId)) {
            throw new PortalException(ErrCode.WORKFLOW_INSTANCEID_EMPTY.getErrCode(),
                ErrCode.WORKFLOW_INSTANCEID_EMPTY.getErrMsg());
        }
        if (log.isDebugEnabled()) {
            log.debug("\n调用引擎接口【getWorkflowOperationLogsByInstanceId()】,参数：workflowInstanceId=【{}】",
                workflowInstanceId);
        }
        List<BizObjectLogModel> bizObjectLogList = getWorkflowInstanceFacade().queryBizObjectLogs(workflowInstanceId);
        return getOkResponseResult(bizObjectLogList, "成功返回流程操作日志");
    }

    @ApiOperation(value = "比较修改前后日志", notes = "比较修改前后日志")
    @GetMapping("/compareOperationLog")
    @ApiImplicitParam(name = "bizObjectLogId", value = "表单操作日志id", required = true, dataType = "String",
        paramType = "query")
    public ResponseResult<CompareOperationLogVO> compareOperationLog(@RequestParam String bizObjectLogId) {
        if (Strings.isNullOrEmpty(bizObjectLogId)) {
            throw new PortalException(ErrCode.BIZ_OBJECT_ID_EMPTY.getErrCode(), "表单操作日志ID不能为空");
        }

        BizObjectLogModel bizObjectLog = getWorkflowInstanceFacade().getBizObjectLog(bizObjectLogId);
        if (bizObjectLog == null) {
            throw new PortalException(ErrCode.BIZ_OBJECT_ID_EMPTY.getErrCode(), "表单操作日志不存在");
        }
        List<BizObjectLogModel> bizObjectLogList = getWorkflowInstanceFacade()
            .queryBizObjectLogs(bizObjectLog.getWorkflowInstanceId(), bizObjectLog.getOperationType());
        BizObjectLogModel prevBizObjectLog = null;// 修改前日志
        if (CollectionUtils.isNotEmpty(bizObjectLogList)) {
            boolean prev = false;// 是否是上一条
            int size = bizObjectLogList.size();
            for (int i = 0; i < size; i++) {
                BizObjectLogModel bizObjectLogModel = bizObjectLogList.get(i);
                if (prev) {
                    prevBizObjectLog = bizObjectLogModel;
                    break;
                }
                if (bizObjectLogId.equals(bizObjectLogModel.getId())) {
                    prev = true;
                }
            }
        }

        WorkflowInstanceModel workflowInstance =
            getWorkflowInstanceFacade().getWorkflowInstance(bizObjectLog.getWorkflowInstanceId());
        if (workflowInstance == null) {
            throw new PortalException(ErrCode.WORKFLOW_INS_EMPTY);
        }
        String workitemId = null;
        String detail = bizObjectLog.getDetail();
        JSONObject postDetailJson = null;
        if (StringUtils.isNotBlank(detail)) {
            postDetailJson = JSON.parseObject(detail);
            workitemId = postDetailJson.getString("workItemId");
        }

        String userId = this.getUserId();
        FormDataVO formData = new FormDataVO();
        setFormDataByWorkItem(formData, userId, workitemId, workflowInstance.getId(), null, null, null, null, null);
        BizFormModel bizSheet = formData.getBizSheet();

        CompareOperationLogVO compareOperationLogVO = new CompareOperationLogVO();
        compareOperationLogVO.setBizSheet(bizSheet);
        compareOperationLogVO.setPostBizObjectLog(postDetailJson);
        if (prevBizObjectLog != null && StringUtils.isNotBlank(prevBizObjectLog.getDetail())) {
            compareOperationLogVO.setPrevBizObjectLog(JSON.parseObject(prevBizObjectLog.getDetail()));
        }

        compareOperationLogVO.setBizSchema(formData.getBizSchema());

        return getOkResponseResult(compareOperationLogVO, "成功返回流程操作日志");
    }

    @ApiOperation(value = "根据流程实例id、tokenId和任务id获取审批意见", notes = "根据流程实例id、tokenId和任务id获取审批意见")
    @ApiImplicitParams({
        @ApiImplicitParam(name = "workflowInstanceId", value = "流程实例ID", dataType = "String", paramType = "query"),
        @ApiImplicitParam(name = "tokenId", value = "tokenId", dataType = "String", paramType = "query"),
        @ApiImplicitParam(name = "workItemId", value = "任务id", dataType = "String", paramType = "query")})
    @GetMapping("/get_comment")
    public ResponseResult<BizCommentModel> getComment(@NotBlank(message = "流程实例id不允许为空") String workflowInstanceId,
        @NotBlank(message = "tokenId不允许为空") String tokenId, String workItemId) {
        String userId = getUserId();

        WorkItemModel itemModel = getWorkflowInstanceFacade().getWorkItem(workItemId);
        if (itemModel == null) {
            return getOkResponseResult(null, "任务不存在.");
        }

        BizCommentModel currentBizComment = null;
        // 登录人是当前任务处理人
        boolean isParticipant = Objects.equals(userId, itemModel.getParticipant());
        // 任务状态是 进行中 未启动
        boolean inProgressOrNotStart =
            itemModel.getState() == WorkItemStatus.INPROGRESS || itemModel.getState() == WorkItemStatus.NOTSTARTED;

        if (isParticipant && inProgressOrNotStart) {
            List<BizCommentModel> currentBizComments = getBizObjectFacade().getComments(workflowInstanceId, tokenId);
            currentBizComment = currentBizComments.stream()
                .filter(c -> Objects.equals(userId, c.getCreater().get("id"))).findAny().orElse(null);
        }

        return getOkResponseResult(currentBizComment, "成功根据流程实例id、tokenId和任务id获取审批意见");
    }

    @ApiOperation(value = "修复异常流程", notes = "修复异常流程")
    @ApiImplicitParam(name = "exceptionLogId", value = "异常日志id", required = true, dataType = "String",
        paramType = "query")
    @GetMapping("/fix_activity")
    public ResponseResult<Boolean> fixActivity(@NotBlank(message = "异常日志id不允许为空") @RequestParam String exceptionLogId) {
        WorkflowExceptionLogDetailModel model = getSystemManagementFacade().getWorkflowExceptionLog(exceptionLogId);
        if (model == null) {
            throw new PortalException(ErrCode.WORKFLOW_EXCEPTION_LOG_NOT_EXIST);
        }
        if (model.getStatus() == WorkflowExceptionLogStatus.RECOVERED) {
            throw new PortalException(ErrCode.WORKFLOW_EXCEPTION_LOG_FIXED);
        }
        String instanceId = model.getWorkflowInstanceId();
        String workflowCode = model.getWorkflowCode();

        // 获取对应版本的流程模板
        WorkflowTemplateModel templateModel =
            getAppManagementFacade().getPublishedWorkflowTemplate(workflowCode, model.getWorkflowVersion());
        if (templateModel == null) {
            throw new PortalException(ErrCode.WORKFLOW_NOT_EXIST);
        }

        // 获取异常节点编码
        List<String> exceptionCodes = getExceptionActivityCode(instanceId);
        // 调用激活接口
        if (CollectionUtils.isEmpty(exceptionCodes)) {
            throw new PortalException(ErrCode.WORKFLOW_EXCEPTION_ACTIVITY_NOT_EXIST);
        }
        String userId = getUserId();
        for (String exceptionCode : exceptionCodes) {
            if (log.isDebugEnabled()) {
                log.debug("开始激活节点, exceptionCode = {}", exceptionCode);
            }
            getWorkflowInstanceFacade().cancelActivity(userId, instanceId, exceptionCode);
            getWorkflowInstanceFacade().activateActivity(userId, instanceId, exceptionCode);

            if (log.isDebugEnabled()) {
                log.debug("开始记录修复异常日志, exceptionCode = {}", exceptionCode);
            }
            getWorkflowInstanceFacade().fixWorkflowExceptionLog(instanceId, userId, "说明信息");
        }
        return getOkResponseResult(Boolean.TRUE, "成功调用修复流程接口");
    }

    /**
     * 获取异常节点编码
     *
     * @param instanceId
     *            流程实例
     * @return List
     */
    private List<String> getExceptionActivityCode(String instanceId) {
        List<String> activityCodes = Lists.newArrayList();
        List<WorkflowTokenModel> workflowTokens = getWorkflowInstanceFacade().getWorkflowTokens(instanceId);
        if (CollectionUtils.isEmpty(workflowTokens)) {
            return activityCodes;
        }
        for (WorkflowTokenModel workflowToken : workflowTokens) {
            if (WorkflowTokenStatus.EXCEPTION == workflowToken.getState()) {
                if (log.isDebugEnabled()) {
                    log.debug("异常节点 = {}", workflowToken.getActivityCode());
                }
                activityCodes.add(workflowToken.getActivityCode());
            }
        }
        return activityCodes;
    }

    @SuppressWarnings("rawtypes")
    @GetMapping("/list_user_business_items")
    @ApiOperation(value = "工作业务数据交接的条件查询", notes = "根据业务模型、单据状态查询所选用户的业务列表数据")
    @ApiImplicitParams({
        @ApiImplicitParam(name = "userId", value = "所选用户id", required = true, dataType = "String", paramType = "query"),
        @ApiImplicitParam(name = "schemaCode", value = "数据模型编码", dataType = "String", paramType = "query"),
        @ApiImplicitParam(name = "sequenceStatus", value = "单据状态", dataType = "String", paramType = "query"),
        @ApiImplicitParam(name = "page", value = "当前页码", dataType = "Integer", paramType = "query"),
        @ApiImplicitParam(name = "size", value = "每页显示数量", dataType = "Integer", paramType = "query")})
    public ResponseResult<PageVO<BizObjectDataItemModel>> listUserBusinessItems(
        @NotBlank(message = "用户ID不能为空！") @RequestParam String userId, @RequestParam(required = false) String schemaCode,
        @RequestParam(required = false) String sequenceStatus, @RequestParam(required = false) Integer page,
        @RequestParam(required = false) Integer size) {
        validateNotEmpty(userId, "需要移交的用户ID不能为空");
        if (log.isDebugEnabled()) {
            log.debug("查询选定用户的待办列表：userId = {}, schemaCode = {}, sequenceStatus = {}, page = {}, size = {}", userId,
                schemaCode, sequenceStatus, page, size);
        }

        // 接收时间倒序
        Pageable pageable = getPageable(page, size, Sort.Direction.DESC, "startTime");

        com.authine.cloudpivot.engine.component.query.api.Page<BizObjectModel> workItemsPage =
            getBizObjectFacade().queryBizObjectItemDatas(userId, schemaCode, sequenceStatus, page, size);
        Page<BizObjectDataItemModel> bizObjectDataItemModels;
        List<BizObjectDataItemModel> dataItemModels = Lists.newArrayList();

        if (workItemsPage != null) {
            List<? extends BizObjectModel> content = workItemsPage.getContent();
            if (!CollectionUtils.isEmpty(content)) {
                for (BizObjectModel bizObjectModel : content) {
                    Map<String, Object> data = bizObjectModel.getData();
                    // 过滤异常流程表单
                    String instanceId = (String)data.get(DefaultPropertyType.WORKFLOW_INSTANCE_ID.getCode());
                    if (StringUtils.isNotBlank(instanceId)) {
                        WorkflowInstanceModel workflowInstance =
                            getWorkflowInstanceFacade().getWorkflowInstance(instanceId);
                        if (workflowInstance != null
                            && WorkflowInstanceStatus.EXCEPTION == workflowInstance.getState()) {
                            continue;
                        }
                    }
                    BizObjectDataItemModel dataItemModel = new BizObjectDataItemModel();
                    dataItemModel.setId((String)data.get(DefaultPropertyType.ID.getCode()));
                    dataItemModel.setSchemaCode(bizObjectModel.getSchemaCode());
                    dataItemModel.setName((String)data.get(DefaultPropertyType.NAME.getCode()));
                    AppFunctionModel appFunctionModel = getAppManagementFacade().getAppFunctionByCode(schemaCode);
                    dataItemModel.setModuleName(appFunctionModel == null ? null : appFunctionModel.getName());
                    dataItemModel.setCreatedTime((Timestamp)data.get(DefaultPropertyType.CREATED_TIME.getCode()));
                    if (data.get(DefaultPropertyType.OWNER_DEPT_ID.getCode()) != null) {
                        dataItemModel.setOwnerDept(
                            ((SelectionValue)((List)data.get(DefaultPropertyType.OWNER_DEPT_ID.getCode())).get(0))
                                .getName());
                    }
                    dataItemModel.setSequenceStatus((String)data.get(DefaultPropertyType.SEQUENCE_STATUS.getCode()));
                    dataItemModels.add(dataItemModel);
                }
            }
            bizObjectDataItemModels = new PageImpl<>(dataItemModels, pageable, workItemsPage.getTotal());
        } else {
            bizObjectDataItemModels = new PageImpl<>(Lists.newArrayList(), pageable, 0L);
        }

        return getOkResponseResult(new PageVO<>(bizObjectDataItemModels), "成功查询选定用户的业务数据列表");
    }

    @GetMapping("/list_user_workitems")
    @ApiOperation(value = "工作交接的条件查询", notes = "根据流程模板、发起人条件查询所选用户的代办列表数据")
    @ApiImplicitParams({
        @ApiImplicitParam(name = "userId", value = "所选用户id", required = true, dataType = "String", paramType = "query"),
        @ApiImplicitParam(name = "workflowCode", value = "流程编码", dataType = "String", paramType = "query"),
        @ApiImplicitParam(name = "originator", value = "发起人id", dataType = "String", paramType = "query"),
        @ApiImplicitParam(name = "page", value = "当前页码", dataType = "Integer", paramType = "query"),
        @ApiImplicitParam(name = "size", value = "每页显示数量", dataType = "Integer", paramType = "query"),
        @ApiImplicitParam(name = "sequenceNo", value = "单据号", dataType = "String", paramType = "query")})
    public ResponseResult<PageVO<WorkItemVO>> listUserWorkItems(
        @NotBlank(message = "用户ID不能为空！") @RequestParam String userId,
        @RequestParam(required = false) String workflowCode, @RequestParam(required = false) String originator,
        @RequestParam(required = false) Integer page, @RequestParam(required = false) Integer size,
        @RequestParam(required = false) String sequenceNo) {
        if (log.isDebugEnabled()) {
            log.debug("查询选定用户的待办列表：userId = {}, workflowCode = {}, originator = {}, page = {}, size = {}", userId,
                workflowCode, originator, page, size);
        }

        Pageable pageable = getPageable(page, size, Sort.Direction.DESC, "startTime");
        Page<WorkItemModel> workItemsPage = getWorkflowInstanceFacade().queryWorkItems(userId, StringUtils.EMPTY,
            workflowCode, originator, UnitType.USER, null, null, null, null, null, pageable, sequenceNo);
        List<WorkItemModel> workItemModels = workItemsPage.getContent();
        Set<String> originatorDeptIds =
            workItemModels.stream().map(WorkItemModel::getDepartmentId).collect(Collectors.toSet());
        Map<String, List<DepartmentModel>> parentDepts =
            getOrganizationFacade().getDepartmentsByParentIds(Lists.newArrayList(originatorDeptIds));

        List<WorkItemVO> workItems = workItemModels.stream().filter(Objects::nonNull).map(workItemModel -> {
            if (MapUtils.isNotEmpty(parentDepts)) {
                workItemModel.setDepartmentName(getFullDepartmentName(parentDepts, workItemModel.getDepartmentId(),
                    workItemModel.getDepartmentName()));
            }

            WorkItemVO workItemVO = new WorkItemVO(workItemModel);
            BeanUtils.copyProperties(workItemModel, workItemVO);
            WorkflowTemplateHeaderModel header =
                getAppManagementFacade().getWorkflowTemplateHeader(workItemModel.getWorkflowCode());
            workItemVO.setWorkflowName(header == null ? StringUtils.EMPTY : header.getWorkflowName());
            workItemVO.setImgUrl(getUserImg(workItemModel.getOriginator()));
            return workItemVO;
        }).collect(Collectors.toList());

        Page<WorkItemVO> workItemVOPage = new PageImpl<>(workItems, pageable, workItemsPage.getTotalElements());
        return getOkResponseResult(new PageVO<>(workItemVOPage), "成功查询选定用户的待办列表");
    }

    @SuppressWarnings("rawtypes")
    @PostMapping("/transfer")
    @ApiOperation(value = "工作交接已选任务或全部任务", notes = "工作交接已选任务或全部任务")
    public ResponseResult transferWorkItems(@RequestBody TransferVO transferVO) {
        checkUser(transferVO.getUserId());
        checkUser(transferVO.getTransferUserId());
        if (transferVO.getUserId().equals(transferVO.getTransferUserId())) {
            return getErrResponseResult(ErrCode.WORKFLOW_CANNOT_FORWARD_ONESELF.getErrCode(),
                ErrCode.WORKFLOW_CANNOT_FORWARD_ONESELF.getErrMsg());
        }
        TransferResultVO transferResult = new TransferResultVO();
        Integer success = 0;
        Integer fails = 0;
        transferResult.setSuccess(success);
        transferResult.setFails(fails);
        // 业务数据工作交接
        if (transferVO.getIsBusiness() != null && transferVO.getIsBusiness()) {
            transferVO.setOriginator(getUserId());
            if (transferVO.getTransferAll() != null && transferVO.getTransferAll()
                && StringUtils.isEmpty(transferVO.getSchemaCode())) {
                if (isProc) {
                    return getErrResponseResult(ErrCode.SYS_BUSINESS_DOING_ERROR.getErrCode(),
                        ErrCode.SYS_BUSINESS_DOING_ERROR.getErrMsg());
                }
                isProc = Boolean.TRUE;
                new Thread(() -> {
                    try {
                        transferBusiness(transferVO, transferResult);
                        log.info("全量工作交接完成,结果为{}", transferResult);
                        sendDingMessage(transferVO, transferResult);
                    } catch (Exception e) {
                        log.error("全量工作交接失败,", e);
                    } finally {
                        isProc = Boolean.FALSE;
                    }
                }).start();
                return getOkResponseResult(null, "工作交接已在运行");
            } else {
                transferBusiness(transferVO, transferResult);
                sendDingMessage(transferVO, transferResult);
                return getOkResponseResult(transferResult, "工作交接已选任务或全部任务");
            }
        }
        String comment = "这是一条工作交接待办任务.";
        // 原任务数据交接
        List<WorkItemVO> workItems = Lists.newArrayList();
        if (transferVO.getTransferAll()) {
            // 交接筛选全部
            Integer size = Integer.MAX_VALUE;
            // 接收时间倒序
            Pageable pageable = getPageable(0, size, Sort.Direction.DESC, "startTime");
            Page<WorkItemModel> workItemsPage = getWorkflowInstanceFacade().queryWorkItems(transferVO.getUserId(),
                StringUtils.EMPTY, transferVO.getWorkflowCode(), transferVO.getOriginator(), UnitType.USER, null, null,
                null, null, null, pageable, "");
            if (log.isDebugEnabled()) {
                log.debug("结束查询全部待办，开始进行交接...size = {}, workItemsTotalPageSize = {}", size,
                    workItemsPage.getTotalPages());
            }
            List<WorkItemModel> workItemModels = workItemsPage.getContent();
            if (CollectionUtils.isEmpty(workItemModels)) {
                throw new PortalException(ErrCode.WORKFLOW_WORK_ITEM_EMPTY);
            }
            for (WorkItemModel model : workItemModels) {
                try {
                    Boolean forwardResult = forwardWordItemWithNoMessage(transferVO.getUserId(), model.getId(),
                        transferVO.getTransferUserId(), transferVO.getTransferDeptId(), comment);
                    if (log.isDebugEnabled()) {
                        log.debug("全部交接, forwardResult = {}, success = {}, fails = {}", forwardResult, success, fails);
                    }

                    if (forwardResult) {
                        transferResult.setSuccess(transferResult.getSuccess() + 1);
                        writeOperationLog(model.getInstanceId(), model.getId(), model.getActivityCode(),
                            OperationType.WORK_ITEM_HANDOVER, transferVO);
                    } else {
                        transferResult.setFails(transferResult.getFails() + 1);
                        WorkItemVO workItemVO = new WorkItemVO(null);
                        BeanUtils.copyProperties(model, workItemVO);
                        workItems.add(workItemVO);
                    }
                } catch (Exception e) {
                    log.error("userId = {}, id = {}, transferUserId = {}", transferVO.getUserId(), model.getId(),
                        transferVO.getTransferUserId());
                    log.error("交接全部工作时异常，e = {}", e);
                    transferResult.setFails(transferResult.getFails() + 1);
                    WorkItemVO workItemVO = new WorkItemVO(null);
                    BeanUtils.copyProperties(model, workItemVO);
                    workItems.add(workItemVO);
                }

            }
        } else {
            // 交接部分
            if (CollectionUtils.isEmpty(transferVO.getWorkItemIds())) {
                throw new PortalException(ErrCode.FORWARD_WORKITEM_ID_EMPTY);
            }
            for (String workItemId : transferVO.getWorkItemIds()) {
                try {
                    WorkItemModel workItem = getWorkflowInstanceFacade().getWorkItem(workItemId);
                    Boolean forwardResult = forwardWordItemWithNoMessage(transferVO.getUserId(), workItemId,
                        transferVO.getTransferUserId(), transferVO.getTransferDeptId(), comment);
                    if (forwardResult) {
                        transferResult.setSuccess(transferResult.getSuccess() + 1);
                        if (workItem != null) {
                            writeOperationLog(workItem.getInstanceId(), workItem.getId(), workItem.getActivityCode(),
                                OperationType.WORK_ITEM_HANDOVER, transferVO);
                        }
                    } else {
                        transferResult.setFails(transferResult.getFails() + 1);
                        WorkItemModel itemModel = getWorkflowInstanceFacade().getWorkItem(workItemId);
                        WorkItemVO workItemVO = new WorkItemVO(null);
                        if (itemModel != null) {
                            BeanUtils.copyProperties(itemModel, workItemVO);
                        } else {
                            workItemVO.setId(workItemId);
                        }
                        workItems.add(workItemVO);
                    }
                } catch (Exception e) {
                    log.error("userId = {}, id = {}, transferUserId = {}", transferVO.getUserId(), workItemId,
                        transferVO.getTransferUserId());
                    log.error("交接部分工作时异常，e = {}", e);
                    transferResult.setFails(transferResult.getFails() + 1);
                    WorkItemModel itemModel = getWorkflowInstanceFacade().getWorkItem(workItemId);
                    WorkItemVO workItemVO = new WorkItemVO(null);
                    if (itemModel != null) {
                        BeanUtils.copyProperties(itemModel, workItemVO);
                    } else {
                        workItemVO.setId(workItemId);
                    }
                    workItems.add(workItemVO);
                }
            }
        }
        transferResult.setWorkItems(workItems);
        sendDingMessage(transferVO, transferResult);
        return getOkResponseResult(transferResult, "工作交接已选任务或全部任务");
    }

    private void sendDingMessage(TransferVO transferVO, TransferResultVO transferResult) {
        // 给接收人发送通知消息
        UserModel transUserModel = getOrganizationFacade().getUser(transferVO.getTransferUserId());
        if (transUserModel == null || StringUtils.isBlank(transUserModel.getUserId())) {
            log.info("发送失败：当前用户/userId {} 不存在", transferVO.getTransferUserId());
            return;
        }

        String title = "工作交接提醒!";
        UserModel userModel = getOrganizationFacade().getUser(transferVO.getUserId());
        if (transferResult.getSuccess() > 0) {
            String content = "管理员将" + userModel.getName() + "的" + transferResult.getSuccess() + "条"
                + (transferVO.getIsBusiness() ? "数据" : "待办任务") + "交接给了你,请尽快处理!";
            content = title + "\n\n" + content;
            MessageHistoryModel model =
                getMessageModel(title, content, transUserModel.getId(), transUserModel.getCorpId());
            getWorkflowInstanceFacade().sendMessage(model);
        }

        // 给发起人发送通知消息
        // UserModel operator = getOrganizationFacade().getUser(transferVO.getOriginator());
        // 跟现有测试确认，测试跟之前测试这块的测试确认，给当前登录的人发消息，发起人不用发消息
        UserModel operator = getOrganizationFacade().getUser(getUserId());
        if (null == operator || StringUtils.isEmpty(operator.getUserId())) {
            return;
        }

        if (StringUtils.isNotEmpty(operator.getSourceId()) && !"admin".equals(operator.getUsername())) {
            String sysContent = "你于" + DateUtil.format(transferVO.getBeginTime(), DatePattern.NORM_DATETIME_FORMAT)
                + "发起的【" + userModel.getName() + "】工作交接已完成！成功" + transferResult.getSuccess() + "条,失败"
                + transferResult.getFails() + "条";
            sysContent = title + "\n\n" + sysContent;
            MessageHistoryModel sysMessageModel =
                getMessageModel(title, sysContent, operator.getId(), operator.getCorpId());
            if (transferResult.getSuccess() <= 0 && transferResult.getFails() <= 0) {
                sysMessageModel
                    .setContent("你于" + DateUtil.format(transferVO.getBeginTime(), DatePattern.NORM_DATETIME_FORMAT)
                        + "发起的【" + userModel.getName() + "】工作交接失败！当前用户没有数据可交接");
            }
            getWorkflowInstanceFacade().sendMessage(sysMessageModel);
        }
    }

    private MessageHistoryModel getMessageModel(String title, String content, String userId, String corpId) {
        if (log.isDebugEnabled()) {
            log.debug("message receiver = {}.", userId);
        }
        MessageHistoryModel model = new MessageHistoryModel();
        String id = UUID.randomUUID().toString().replaceAll("-", "");
        model.setTitle(title);
        model.setContent(content);
        model.setId(id);
        model.setReceivers(userId);
        model.setUrl("?messageId=" + id);
        model.setFailRetry(Boolean.TRUE);
        model.setFailUserRetry(Boolean.TRUE);
        model.setChannel(IMMessageChannelType.DINGTALK);
        model.setMessageType(IMMessageType.BUSINESS_REMIND);

        Map<String, Object> params = Maps.newHashMap();
        params.put("corpId", corpId);
        model.setBizParams(JSON.toJSONString(params));
        return model;
    }

    /**
     * 交接业务数据
     *
     * @param transferVO
     */
    private void transferBusiness(TransferVO transferVO, TransferResultVO resultVO) {
        // 全量交接
        if (transferVO.getTransferAll() != null && transferVO.getTransferAll()) {
            String schemaCode = transferVO.getSchemaCode();
            if (StringUtils.isBlank(schemaCode)) {
                // 获取所有的i表记录
                List<BizSchemaModel> bizSchemaModels = getAppManagementFacade().getAllPublishBizSchema();
                if (CollectionUtils.isNotEmpty(bizSchemaModels)) {
                    bizSchemaModels.forEach(bizSchemaModel -> {
                        transferVO.setSchemaCode(bizSchemaModel.getCode());
                        log.info("正在交接模型：{}", bizSchemaModel.getCode());
                        com.authine.cloudpivot.engine.component.query.api.Page<BizObjectModel> bizObjectModelPage =
                            getBizObjectFacade().queryBizObjectItemDatas(transferVO.getUserId(),
                                bizSchemaModel.getCode(), StringUtils.EMPTY, 0, Integer.MAX_VALUE);
                        transferByPage(bizObjectModelPage, transferVO, resultVO);
                    });
                }
            } else {
                com.authine.cloudpivot.engine.component.query.api.Page<BizObjectModel> workItemsPage =
                    getBizObjectFacade().queryBizObjectItemDatas(transferVO.getUserId(), schemaCode,
                        transferVO.getSequenceStatus(), 0, Integer.MAX_VALUE);
                transferByPage(workItemsPage, transferVO, resultVO);
            }
        } else {
            // 部分交接
            List<String> workItemIds = transferVO.getWorkItemIds();
            for (String id : workItemIds) {
                try {
                    Boolean forwardResult = transferBussinessItem(transferVO, id);
                    if (forwardResult) {
                        resultVO.setSuccess(resultVO.getSuccess() + 1);
                    } else {
                        resultVO.setFails(resultVO.getFails() + 1);
                    }
                } catch (Exception e) {
                    log.error("userId = {}, id = {}, transferUserId = {}", transferVO.getUserId(), id,
                        transferVO.getTransferUserId());
                    log.error("交接部分业务数据时异常，e = {}", e);
                    resultVO.setFails(resultVO.getFails() + 1);
                }
            }
        }
    }

    private void transferByPage(
        com.authine.cloudpivot.engine.component.query.api.Page<BizObjectModel> bizObjectModelPage,
        TransferVO transferVO, TransferResultVO resultVO) {
        if (bizObjectModelPage != null && CollectionUtils.isNotEmpty(bizObjectModelPage.getContent())) {
            List<? extends BizObjectModel> content = bizObjectModelPage.getContent();
            for (BizObjectModel bizObjectModel : content) {
                Map<String, Object> data = bizObjectModel.getData();
                String bizId = (String)data.get(DefaultPropertyType.ID.getCode());
                Boolean forwardResult = false;
                if (StringUtils.isNotBlank(bizId)) {
                    forwardResult = transferBussinessItem(transferVO, bizId);
                }
                if (forwardResult) {
                    resultVO.setSuccess(resultVO.getSuccess() + 1);
                } else {
                    resultVO.setFails(resultVO.getFails() + 1);
                }
            }
        }
    }

    private Boolean transferBussinessItem(TransferVO transferVO, String bizId) {
        Boolean isSuccess = false;
        try {
            BizObjectCreatedModel bizObjectCreatedModel =
                getBizObjectFacade().getBizObject(transferVO.getOriginator(), transferVO.getSchemaCode(), bizId);
            if (bizObjectCreatedModel != null && bizObjectCreatedModel.getData() != null
                && bizObjectCreatedModel.getData().size() > 0) {
                bizObjectCreatedModel.setOwner(transferVO.getTransferUserId());
                bizObjectCreatedModel.setOwnerDeptId(transferVO.getTransferDeptId());
                Boolean syncInstance = transferVO.getIsSyncInstance();
                syncInstance = syncInstance == null ? false : syncInstance;
                String newId =
                    getBizObjectFacade().saveBizObject(transferVO.getOriginator(), bizObjectCreatedModel, syncInstance);
                if (StringUtils.isNotBlank(newId)) {
                    isSuccess = true;
                }
                if (StringUtils.isNotEmpty(bizObjectCreatedModel.getWorkflowInstanceId())) {
                    writeOperationLog(bizObjectCreatedModel.getWorkflowInstanceId(), bizId, null,
                        OperationType.DATA_HANDOVER, transferVO);

                }
            }
        } catch (Exception e) {
            log.error("业务转办数据:{},异常-{}", bizId, e);
        }
        return isSuccess;
    }

    private void checkUser(String userId) {
        validateNotEmpty(userId, ErrCode.ORG_USER_ID_EMPTY.getErrMsg());
        UserModel userModel = getOrganizationFacade().getUser(userId);
        if (userModel == null) {
            throw new PortalException(ErrCode.ORG_USER_NONEXISTENT);
        }
    }

    @ApiOperation(value = "判断任务是否可以取回")
    @ApiImplicitParams({
        @ApiImplicitParam(name = "workflowInstanceId", value = "流程实例id", dataType = "String", paramType = "query",
            required = true),
        @ApiImplicitParam(name = "activityCode", value = "节点编码", dataType = "String", paramType = "query",
            required = true)})
    @GetMapping("/is_retrieve")
    public ResponseResult<Boolean> isRetrieve(String workflowInstanceId, String activityCode) {
        if (log.isDebugEnabled()) {
            log.debug("判断任务是否可以取回. workflowInstanceId = {}, activityCode = {}", workflowInstanceId, activityCode);
        }

        validateNotEmpty(workflowInstanceId, "流程实例id不能为空");
        validateNotEmpty(activityCode, "拥有者id不能为空");

        Boolean isRetrieve = getWorkflowInstanceFacade().isRetrieve(getUserId(), workflowInstanceId, activityCode);

        return getOkResponseResult(isRetrieve, "查询成功");
    }

    @ApiOperation(value = "根据流程实例id获取最新的待办任务")
    @ApiImplicitParam(name = "workflowInstanceId", value = "流程实例id", dataType = "String", paramType = "query",
        required = true)
    @GetMapping("/get_workitem_by_instanceid")
    public ResponseResult<WorkItemModel> getWorkItemByInstanceId(String workflowInstanceId) {
        if (log.isDebugEnabled()) {
            log.debug("根据流程实例id获取最新的待办任务. workflowInstanceId = {}", workflowInstanceId);
        }

        validateNotEmpty(workflowInstanceId, "流程实例id不能为空");

        // final WorkItemModel workItemModel = getWorkItemByInstanceIdAndUserId(getUserId(), workflowInstanceId);

        final List<WorkItemModel> itemModels = getWorkflowInstanceFacade().getWorkItems(workflowInstanceId, false);
        if (CollectionUtils.isEmpty(itemModels)) {
            return getOkResponseResult(null, "查询成功");
        }

        final List<WorkItemModel> tempList = itemModels.stream()
            .sorted(Comparator.comparing(WorkItemModel::getStartTime).reversed()).collect(Collectors.toList());

        return getOkResponseResult(tempList.get(0), "查询成功");
    }

    @GetMapping("/simulative/search_task")
    @ApiOperation(value = "按节点查询模拟流程任务列表", notes = "按节点查询模拟流程任务数据")
    @ApiImplicitParams({
        @ApiImplicitParam(name = "workflowInstanceId", value = "流程实例id", required = true, dataType = "String",
            paramType = "query"),
        @ApiImplicitParam(name = "activityCode", value = "流程节点编码", required = true, dataType = "String",
            paramType = "query")
    // @ApiImplicitParam(name = "page", value = "当前页码", required = false, dataType = "Integer", paramType = "query"),
    // @ApiImplicitParam(name = "size", value = "每页显示数量", required = false, dataType = "Integer", paramType = "query")
    })
    public ResponseResult<List<WorkItemVO>> searchTask(
        @RequestParam(value = "workflowInstanceId", required = true) String workflowInstanceId,
        @RequestParam(value = "activityCode", required = true) String activityCode) {
        WorkflowInstanceModel instanceModel = getWorkflowInstanceFacade().getWorkflowInstance(workflowInstanceId);
        if (instanceModel == null) {
            throw new PortalException(ErrCode.WORKFLOW_INS_EMPTY);
        }
        AppFunctionModel appFunction = getAppManagementFacade().getAppFunctionByCode(instanceModel.getSchemaCode());
        if (appFunction == null) {
            throw new PortalException(ErrCode.APP_FUNCTION_MODEL_NOTEXIST,
                ErrCode.APP_FUNCTION_MODEL_NOTEXIST.getErrMsg());
        }
        final SingleActivityWorkflowTemplateModel singleActivityWorkflowTemplateModel =
            getAppManagementFacade().getSingleActivityWorkflowTemplate(instanceModel.getWorkflowCode(),
                instanceModel.getWorkflowVersion(), activityCode);
        if (singleActivityWorkflowTemplateModel == null) {
            return getOkResponseResult(null, "查询待办数据成功");
        }

        List<WorkItemVO> results = Lists.newArrayList();
        if (singleActivityWorkflowTemplateModel != null
            && singleActivityWorkflowTemplateModel.getActivity() instanceof ParticipantActivity) {
            final Boolean isStart = singleActivityWorkflowTemplateModel.isOriginateActivity();
            final ParticipantActivity participantActivity =
                (ParticipantActivity)singleActivityWorkflowTemplateModel.getActivity();
            // ActionPermissionVO actionPermissionVO = this.getActionPermissionFromInprogress(participantActivity,
            // isStart);

            List<WorkItemModel> workItemModels = getWorkflowInstanceFacade().getWorkItems(workflowInstanceId, false);// 待办
            List<CirculateItemModel> circulateItemModels =
                getWorkflowInstanceFacade().getCirculateItems(workflowInstanceId, false);// 待阅

            workItemModels = workItemModels == null ? Lists.newArrayList()
                : workItemModels.stream().filter(workItemModel -> activityCode.equals(workItemModel.getActivityCode()))
                    .collect(Collectors.toList());
            circulateItemModels = circulateItemModels == null ? Lists.newArrayList()
                : circulateItemModels.stream()
                    .filter(workItemModel -> activityCode.equals(workItemModel.getActivityCode()))
                    .collect(Collectors.toList());

            workItemModels.stream().forEach(workItemModel -> {
                WorkItemVO workItemVO = new WorkItemVO(workItemModel);
                workItemVO.setCirculate(false);

                final FormPermissionVO formPermissionVO = new FormPermissionVO();
                setActionPermissionFromWorkItem(formPermissionVO, workItemModel.getParticipant(),
                    singleActivityWorkflowTemplateModel, workItemModel, instanceModel, participantActivity,
                    appFunction);
                workItemVO.setActionPermissionVO(formPermissionVO.getActionPermission());
                results.add(workItemVO);
            });

            circulateItemModels.stream().forEach(circulateItemModel -> {
                WorkItemVO workItemVO = new WorkItemVO(circulateItemModel);
                workItemVO.setCirculate(true);
                results.add(workItemVO);
            });
        } else if (singleActivityWorkflowTemplateModel.getActivity() instanceof SubInstanceActivity) {
            List<WorkflowTokenModel> tokenModels = getWorkflowInstanceFacade().getWorkflowTokens(workflowInstanceId);
            tokenModels = tokenModels == null ? Lists.newArrayList()
                : tokenModels.stream()
                    .filter(workflowTokenModel -> activityCode.equals(workflowTokenModel.getActivityCode()))
                    .collect(Collectors.toList());
            Set<String> wokflowTokenIds =
                tokenModels.stream().map(WorkflowTokenModel::getId).collect(Collectors.toSet());

            List<WorkflowInstanceModel> childWorkflowInstances =
                getWorkflowInstanceFacade().getWorkflowInstanceByParentId(workflowInstanceId);
            if (childWorkflowInstances != null) {
                Map<String, WorkflowTemplateHeaderModel> workflowTemplateHeaderModelMap = Maps.newHashMap();
                childWorkflowInstances.stream().forEach(workflowInstanceModel -> {
                    if (wokflowTokenIds.contains(workflowInstanceModel.getWorkflowTokenId())) {
                        WorkItemModel workItemModel = new WorkItemModel();
                        workItemModel.setInstanceName(workflowInstanceModel.getInstanceName());
                        workItemModel.setOriginator(workflowInstanceModel.getOriginator());
                        workItemModel.setOriginatorName(workflowInstanceModel.getOriginatorName());
                        WorkItemVO workItemVO = new WorkItemVO(workItemModel);
                        String workflowCode = workflowInstanceModel.getWorkflowCode();
                        WorkflowTemplateHeaderModel workflowTemplateHeaderModel = null;
                        if (workflowTemplateHeaderModelMap.containsKey(workflowCode)) {
                            workflowTemplateHeaderModel = workflowTemplateHeaderModelMap.get(workflowCode);
                        } else {
                            workflowTemplateHeaderModel = getAppManagementFacade()
                                .getWorkflowTemplateHeader(workflowInstanceModel.getWorkflowCode());
                            workflowTemplateHeaderModelMap.put(workflowCode, workflowTemplateHeaderModel);
                        }

                        if (workflowTemplateHeaderModel != null) {
                            workItemVO.setWorkflowName(workflowTemplateHeaderModel.getWorkflowName());
                        }
                        results.add(workItemVO);
                    }
                });
                workflowTemplateHeaderModelMap.clear();
            }
        } else if (singleActivityWorkflowTemplateModel.getActivity() instanceof CirculateActivity) {
            List<CirculateItemModel> circulateItemModels =
                getWorkflowInstanceFacade().getCirculateItems(workflowInstanceId, false);// 待阅
            circulateItemModels = circulateItemModels == null ? Lists.newArrayList()
                : circulateItemModels.stream()
                    .filter(workItemModel -> activityCode.equals(workItemModel.getActivityCode()))
                    .collect(Collectors.toList());
            circulateItemModels.stream().forEach(circulateItemModel -> {
                WorkItemVO workItemVO = new WorkItemVO(circulateItemModel);
                workItemVO.setCirculate(true);
                results.add(workItemVO);
            });
        }

        return getOkResponseResult(results, "按节点查询模拟流程任务数据成功");
    }

    @SuppressWarnings("rawtypes")
    @DeleteMapping("/deleteCanceledWorkflowInstances")
    @ApiOperation(value = "根据作废流程任务Id删除数据", notes = "根据作废流程任务Id删除数据")
    @ApiImplicitParam(name = "workflowInstanceIds", value = "流程实例ids", required = true, dataType = "List<String>",
        paramType = "query")
    public ResponseResult deleteCanceledWorkflowInstances(@RequestBody List<String> workflowInstanceIds) {
        // 判断参数是否为空
        log.info("workflowInstanceIds:{}", workflowInstanceIds);
        if (workflowInstanceIds.isEmpty()) {
            throw new PortalException(ErrCode.SYS_PARAMETER_EMPTY.getErrCode(),
                ErrCode.SYS_PARAMETER_EMPTY.getErrMsg());
        }
        // 判断用户是否系统管理员
        String userId = AuthUtils.getUserId();
        log.info("userId:{}", userId);
        List<AdminType> adminTypes = getAdminTypes(userId);
        log.info("isAdmin(userId):{}", isAdmin(userId));
        log.info("isSysAdmin(adminTypes):{}", isSysAdmin(adminTypes));
        if (!isAdmin(userId) && !isSysAdmin(adminTypes)) {
            throw new PortalException(ResultEnum.NO_PERMISSION.getErrCode(), ResultEnum.NO_PERMISSION.getErrMsg(),
                AuthUtils.getUserId());
        }
        // 判断流程是否作废流程
        List<WorkflowInstanceModel> instanceModels =
            getWorkflowInstanceFacade().getWorkflowInstanceByIds(workflowInstanceIds);
        log.info("instanceModels:{}", instanceModels);
        if (CollectionUtils.isEmpty(instanceModels)) {
            throw new PortalException(ErrCode.WORKFLOW_ACTIVITY_NOT_EXIST);
        }
        instanceModels.forEach(model -> {
            if (!WorkflowInstanceStatus.CANCELED.equals(model.getState())) {
                throw new PortalException(ErrCode.WORKFLOW_INS_STATE_NOT_CANCELED.getErrCode(),
                    model.getInstanceName() + ErrCode.WORKFLOW_INS_STATE_NOT_CANCELED.getErrMsg());
            }
        });

        boolean result = getWorkflowInstanceFacade().deleteCanceledWorkflowInstancesByIds(workflowInstanceIds,
            WorkflowInstanceStatus.CANCELED);

        return getOkResponseResult(result, result ? "删除成功" : "删除失败");

    }

    private List<AdminType> getAdminTypes(String userId) {
        List<AdminModel> managers = dubboConfigService.getPermissionManagementFacade().getAdminByUserId(userId);
        return managers.stream().map(AdminModel::getAdminType).collect(Collectors.toList());
    }

    private boolean isSysAdmin(List<AdminType> adminTypes) {
        if (adminTypes.contains(AdminType.SYS_MNG)) {
            return true;
        }
        return false;
    }
}